pg.ddx.io pgsql-hackers@postgresql.org mailing list archive
help / color / mirror / Atom feedrange_agg
152+ messages / 16 participants
[nested] [flat]
* range_agg
@ 2019-05-03 22:56 Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-04 01:41 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-04 22:11 ` Re: range_agg Corey Huinker <corey.huinker@gmail.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-23 22:32 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 4 replies; 152+ messages in thread
From: Paul Jungwirth @ 2019-05-03 22:56 UTC (permalink / raw)
To: pgsql-hackers
Hello,
I wrote an extension to add a range_agg function with similar behavior
to existing *_agg functions, and I'm wondering if folks would like to
have it in core? Here is the repo: https://github.com/pjungwir/range_agg
I'm also working on a patch for temporal foreign keys, and having
range_agg would make the FK check easier and faster, which is why I'd
like to get it added. But also it just seems useful, like array_agg,
json_agg, etc.
One question is how to aggregate ranges that would leave gaps and/or
overlaps. So in my extension there is a one-param version that forbids
gaps & overlaps, but I let you permit them by passing extra parameters,
so the signature is:
range_agg(r anyrange, permit_gaps boolean, permit_overlaps boolean)
Perhaps another useful choice would be to return NULL if a gap/overlap
is found, so that each param would have three choices instead of just
two: accept the inputs, raise an error, return a NULL.
What do people think? I plan to work on a patch regardless, so that I
can use it for temporal FKs, but I'd appreciate some feedback on the
"user interface".
Thanks,
--
Paul ~{:-)
pj@illuminatedcomputing.com
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
@ 2019-05-04 01:41 ` David Fetter <david@fetter.org>
2019-05-06 18:19 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
3 siblings, 1 reply; 152+ messages in thread
From: David Fetter @ 2019-05-04 01:41 UTC (permalink / raw)
To: Paul Jungwirth <pj@illuminatedcomputing.com>; +Cc: pgsql-hackers
On Fri, May 03, 2019 at 03:56:41PM -0700, Paul Jungwirth wrote:
> Hello,
>
> I wrote an extension to add a range_agg function with similar behavior to
> existing *_agg functions, and I'm wondering if folks would like to have it
> in core? Here is the repo: https://github.com/pjungwir/range_agg
>
> I'm also working on a patch for temporal foreign keys, and having range_agg
> would make the FK check easier and faster, which is why I'd like to get it
> added. But also it just seems useful, like array_agg, json_agg, etc.
>
> One question is how to aggregate ranges that would leave gaps and/or
> overlaps.
This suggests two different ways to extend ranges over aggregation:
one which is a union of (in general) disjoint intervals, two others
are a union of intervals, each of which has a weight. Please pardon
the ASCII art.
The aggregation of:
[1, 4)
[2, 5)
[8, 10)
could turn into:
{[1,5), [8, 10)} (union without weight)
{{[1,2),1}, {[2,4),2}, {[4,5),1}, {[8,10),1}} (strictly positive weights which don't (in general) cover the space)
{{[1,2),1}, {[2,4),2}, {[4,5),1}, {[5,8),0}, {[8,10),1}} (non-negative weights which guarantee the space is covered)
There is no principled reason to choose one over the others.
> What do people think? I plan to work on a patch regardless, so that I can
> use it for temporal FKs, but I'd appreciate some feedback on the "user
> interface".
I think the cases above, or at least the first two of them, should be
available. They could be called range_agg, weighted_range_agg, and
covering_range_agg.
Best,
David.
--
David Fetter <david(at)fetter(dot)org> http://fetter.org/
Phone: +1 415 235 3778
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-04 01:41 ` Re: range_agg David Fetter <david@fetter.org>
@ 2019-05-06 18:19 ` Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-06 22:07 ` Re: range_agg David Fetter <david@fetter.org>
0 siblings, 1 reply; 152+ messages in thread
From: Paul Jungwirth @ 2019-05-06 18:19 UTC (permalink / raw)
To: David Fetter <david@fetter.org>; +Cc: pgsql-hackers
On 5/3/19 6:41 PM, David Fetter wrote:
> This suggests two different ways to extend ranges over aggregation:
> one which is a union of (in general) disjoint intervals, two others
> are a union of intervals, each of which has a weight.
> . . .
> I think the cases above, or at least the first two of them, should be
> available. They could be called range_agg, weighted_range_agg, and
> covering_range_agg.
Thanks David! I think these two new functions make sense. Before I
implement them too I wonder if anyone else has uses for them?
Thanks,
--
Paul ~{:-)
pj@illuminatedcomputing.com
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-04 01:41 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 18:19 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
@ 2019-05-06 22:07 ` David Fetter <david@fetter.org>
2019-05-06 23:21 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: David Fetter @ 2019-05-06 22:07 UTC (permalink / raw)
To: Paul Jungwirth <pj@illuminatedcomputing.com>; +Cc: pgsql-hackers
On Mon, May 06, 2019 at 11:19:27AM -0700, Paul Jungwirth wrote:
> On 5/3/19 6:41 PM, David Fetter wrote:
> > This suggests two different ways to extend ranges over aggregation:
> > one which is a union of (in general) disjoint intervals, two others
> > are a union of intervals, each of which has a weight.
> > . . .
> > I think the cases above, or at least the first two of them, should be
> > available. They could be called range_agg, weighted_range_agg, and
> > covering_range_agg.
>
> Thanks David! I think these two new functions make sense. Before I implement
> them too I wonder if anyone else has uses for them?
I suspect that if you build it, the will come, "they" being anyone who
has to schedule coverage, check usage of a resource over time, etc. Is
this something you want help with at some level? Coding, testing,
promoting...
Best,
David.
--
David Fetter <david(at)fetter(dot)org> http://fetter.org/
Phone: +1 415 235 3778
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-04 01:41 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 18:19 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-06 22:07 ` Re: range_agg David Fetter <david@fetter.org>
@ 2019-05-06 23:21 ` Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-09 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul Jungwirth @ 2019-05-06 23:21 UTC (permalink / raw)
To: David Fetter <david@fetter.org>; +Cc: pgsql-hackers
> I suspect that if you build it, the will come, "they" being anyone who
> has to schedule coverage, check usage of a resource over time, etc. Is
> this something you want help with at some level? Coding, testing,
> promoting...
You might be right. :-) Most of this is done already, since it was
largely copy/paste from my extension plus figuring out how to register
built-in functions with the .dat files. I need to write some docs and do
some cleanup and I'll have a CF entry. And I'll probably go ahead and
add your two suggestions too.... Things I'd love help with:
- Getting more opinions about the functions' interface, either from you
or others, especially:
- In the extension I have a boolean param to let you accept gaps or
raise an error, and another for overlaps. But what about
accepting/raising/returning null? How should the parameters expose that?
Maybe leave them as bools but accept true/false/null for
permit/raise/nullify respectively? That seems like a questionable UI,
but I'm not sure what would be better. Maybe someone with better taste
can weigh in. :-) I tried to find existing built-in functions that gave
a enumeration of options like that but couldn't find an existing example.
- Also: what do you think of the question I asked in my reply to
Corey? Is it better to have *all* range_agg functions return an array of
ranges, or it is nicer to have a variant that always returns a single range?
- Getting it reviewed.
- Advice about sequencing it with respect to my temporal foreign keys
patch, where I'm planning to call range_agg to check an FK. E.g. should
my FK patch be a diff on top of the range_agg code? I assume they should
have separate CF entries though?
Oh and here's something specific:
- I gave oids to my new functions starting with 8000, because I thought
I saw some discussion about that recently, and the final committer will
correct the oids to the current n+1? But I can't find that discussion
anymore, so if that's the wrong approach let me know.
Thanks!
--
Paul ~{:-)
pj@illuminatedcomputing.com
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-04 01:41 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 18:19 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-06 22:07 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 23:21 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
@ 2019-05-09 04:54 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-06-16 22:59 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2019-05-09 04:54 UTC (permalink / raw)
To: David Fetter <david@fetter.org>; +Cc: pgsql-hackers
On Mon, May 6, 2019 at 4:21 PM Paul Jungwirth
<pj@illuminatedcomputing.com> wrote:
> I need to write some docs and do
> some cleanup and I'll have a CF entry.
Here is an initial patch. I'd love to have some feedback! :-)
One challenge was handling polymorphism, since I want to have this:
anyrange[] range_agg(anyrange, bool, bool)
But there is no anyrange[]. I asked about this back when I wrote my
extension too:
https://www.postgresql.org/message-id/CA%2BrenyVOjb4xQZGjdCnA54-1nzVSY%2B47-h4nkM-EP5J%3D1z%3Db9w%40...
Like then, I handled it by overloading the function with separate
signatures for each built-in range type:
int4range[] range_agg(int4range, bool, bool);
int8range[] range_agg(int8range, bool, bool);
numrange[] range_agg(numrange, bool, bool);
tsrange[] range_agg(tsrange, bool, bool);
tstzrange[] range_agg(tstzrange, bool, bool);
daterange[] range_agg(daterange, bool, bool);
(And users can still define a range_agg for their own custom range
types using similar instructions to those in my extension's README.)
The problem was the opr_sanity regression test, which rejects
functions that share the same C-function implementation (roughly). I
took a few stabs at changing my code to pass that test, e.g. defining
separate wrapper functions for everything like this:
Datum
int4range_agg_transfn(PG_FUNCTION_ARGS) {
return range_agg_transfn(fcinfo);
}
But that felt like it was getting ridiculous (8 range types *
transfn+finalfn * 1-bool and 2-bool variants), so in the end I just
added my functions to the "permitted" output in opr_sanity. Let me
know if I should avoid that though.
Also I would still appreciate some bikeshedding over the functions' UI
since I don't feel great about it.
If the overall approach seems okay, I'm still open to adding David's
suggestions for weighted_range_agg and covering_range_agg.
Thanks!
Paul
Attachments:
[application/octet-stream] range_agg_v1.patch (49.2K, ../../CA+renyVro0sAzUAZucyXGzd+Jkhdb9xvbtwE1QnrpzPcOgc_8Q@mail.gmail.com/2-range_agg_v1.patch)
download | inline diff:
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index da0f305981..bb93b64ee4 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14421,6 +14421,11 @@ NULL baz</literallayout>(3 rows)</entry>
<function>lower_inf</function>, and <function>upper_inf</function>
functions all return false for an empty range.
</para>
+
+ <para>
+ See also <xref linkend="functions-aggregate"/> about the aggregate
+ function <function>range_agg</function> for use with ranges.
+ </para>
</sect1>
<sect1 id="functions-aggregate">
@@ -14739,6 +14744,76 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ any range type
+ </entry>
+ <entry>
+ same as argument type
+ </entry>
+ <entry>No</entry>
+ <entry>input ranges merged into a single range, or an error if there are any gaps or overlaps</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>,
+ <replaceable>permit_gaps</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ (any range type, <type>boolean</type>)
+ </entry>
+ <entry>
+ array of the same range type as argument type
+ </entry>
+ <entry>No</entry>
+ <entry>
+ input ranges merged into a list of non-touching ranges,
+ with gaps permitted, rejected, or yielding a NULL result
+ as the second argument is true, false, or NULL respectively
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>,
+ <replaceable>permit_gaps</replaceable>,
+ <replaceable>permit_overlaps</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ (any range type, <type>boolean</type>, <type>boolean</type>)
+ </entry>
+ <entry>
+ array of the same range type as argument type
+ </entry>
+ <entry>No</entry>
+ <entry>
+ input ranges merged into a list of non-touching ranges,
+ with gaps permitted, rejected, or yielding a NULL result
+ as the second argument is true, false, or NULL respectively,
+ and overlaps permitted, rejected, or yielding a NULL result
+ as the third argument is true, false, or NULL respectively
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>string_agg</primary>
</indexterm>
<function>
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 72c450c70e..020284a386 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -34,6 +34,7 @@
#include "lib/stringinfo.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
+#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/hashutils.h"
@@ -54,6 +55,18 @@ typedef struct RangeIOData
FmgrInfo proc; /* lookup result for typiofunc */
} RangeIOData;
+/* aggregate state for range_agg */
+typedef struct RangeAggState
+{
+ ArrayBuildState *inputs;
+ uint8 flags;
+} RangeAggState;
+#define RANGE_AGG_GAPS_RAISE 0x01
+#define RANGE_AGG_GAPS_SET_NULL 0x02
+#define RANGE_AGG_OVERLAPS_RAISE 0x04
+#define RANGE_AGG_OVERLAPS_SET_NULL 0x08
+#define RANGE_AGG_GAPS_FLAGS (RANGE_AGG_GAPS_RAISE | RANGE_AGG_GAPS_SET_NULL)
+#define RANGE_AGG_OVERLAPS_FLAGS (RANGE_AGG_OVERLAPS_RAISE | RANGE_AGG_OVERLAPS_SET_NULL)
static RangeIOData *get_range_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
IOFuncSelector func);
@@ -69,6 +82,7 @@ static Size datum_compute_size(Size sz, Datum datum, bool typbyval,
char typalign, int16 typlen, char typstorage);
static Pointer datum_write(Pointer ptr, Datum datum, bool typbyval,
char typalign, int16 typlen, char typstorage);
+static int element_compare(const void *key1, const void *key2, void *arg);
/*
@@ -1135,6 +1149,189 @@ range_intersect(PG_FUNCTION_ARGS)
PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
}
+/* Aggregate functions */
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ * We can't simply use array_append as the transfn
+ * because we have to capture the second & third parameters
+ * and put them in the aggregate's running state,
+ * so that our finalfn can use them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ Oid rangeTypeId;
+ MemoryContext aggContext;
+ RangeAggState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ {
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+ }
+
+ rangeTypeId = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rangeTypeId))
+ {
+ ereport(ERROR, (errmsg("range_agg must be called with a range")));
+ }
+ if (PG_ARGISNULL(0))
+ {
+ state = MemoryContextAlloc(aggContext, sizeof(RangeAggState));
+ state->inputs = initArrayResult(rangeTypeId, aggContext, false);
+
+ state->flags = 0x0;
+ switch (PG_NARGS())
+ {
+ case 2:
+ state->flags |= RANGE_AGG_GAPS_RAISE;
+ state->flags |= RANGE_AGG_OVERLAPS_RAISE;
+ break;
+ case 3:
+ if (PG_ARGISNULL(2)) state->flags |= RANGE_AGG_GAPS_SET_NULL;
+ else if (!PG_GETARG_BOOL(2)) state->flags |= RANGE_AGG_GAPS_RAISE;
+ state->flags |= RANGE_AGG_OVERLAPS_RAISE;
+ break;
+ case 4:
+ if (PG_ARGISNULL(2)) state->flags |= RANGE_AGG_GAPS_SET_NULL;
+ else if (!PG_GETARG_BOOL(2)) state->flags |= RANGE_AGG_GAPS_RAISE;
+ if (PG_ARGISNULL(3)) state->flags |= RANGE_AGG_OVERLAPS_SET_NULL;
+ else if (!PG_GETARG_BOOL(3)) state->flags |= RANGE_AGG_OVERLAPS_RAISE;
+ break;
+ default:
+ Assert(false);
+ }
+ }
+ else
+ {
+ state = (RangeAggState *)PG_GETARG_POINTER(0);
+ }
+
+ /* Might as well skip NULLs here so the finalfn doesn't have to: */
+ if (!PG_ARGISNULL(1))
+ {
+ accumArrayResult(state->inputs, PG_GETARG_DATUM(1), false,
+ rangeTypeId, aggContext);
+ }
+
+ PG_RETURN_POINTER(state);
+}
+
+/* range_agg_finalfn: combine adjacent/overlapping ranges */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rangeTypeId;
+ TypeCacheEntry *typcache;
+ RangeAggState *state;
+ ArrayBuildState *inputArray;
+ int inputLength;
+ Datum *inputVals;
+ bool *inputNulls;
+ int i;
+ RangeType *currentRange;
+ RangeType *lastRange;
+ char *r1Str, *r2Str;
+ ArrayBuildState *resultContent;
+ Datum result;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ {
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+ }
+
+ state = PG_ARGISNULL(0) ? NULL : (RangeAggState *)PG_GETARG_POINTER(0);
+ if (state == NULL) PG_RETURN_NULL();
+ inputArray = state->inputs;
+ inputVals = inputArray->dvalues;
+ inputNulls = inputArray->dnulls;
+ inputLength = inputArray->nelems;
+ rangeTypeId = inputArray->element_type;
+
+ typcache = range_get_typcache(fcinfo, rangeTypeId);
+ if (inputLength == 0) PG_RETURN_NULL();
+ qsort_arg(inputVals, inputLength, sizeof(Datum), element_compare, typcache);
+
+ resultContent = initArrayResult(rangeTypeId, aggContext, false);
+ lastRange = DatumGetRangeTypeP(inputVals[0]);
+ for (i = 1; i < inputLength; i++)
+ {
+ Assert(!inputNulls[i]);
+ currentRange = DatumGetRangeTypeP(inputVals[i]);
+ /* range_adjacent_interval gives true
+ * if *either* A meets B or B meets A,
+ * which is not quite what we want,
+ * but we rely on the sorting above
+ * to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(typcache, lastRange, currentRange))
+ {
+ /* The two ranges touch without overlap: */
+ lastRange = range_union_internal(typcache, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(typcache, lastRange, currentRange))
+ {
+ /* There is a gap: */
+ if (state->flags & RANGE_AGG_GAPS_FLAGS)
+ {
+ if (state->flags & RANGE_AGG_GAPS_RAISE)
+ {
+ r1Str = "lastRange";
+ r2Str = "currentRange";
+ // TODO: Why is this segfaulting?:
+ // r1Str = DatumGetCString(DirectFunctionCall1(range_out, RangeTypePGetDatum(lastRange)));
+ // r2Str = DatumGetCString(DirectFunctionCall1(range_out, RangeTypePGetDatum(currentRange)));
+ ereport(ERROR, (errmsg("range_agg: gap detected between %s and %s", r1Str, r2Str)));
+ }
+ else
+ {
+ Assert(state->flags & RANGE_AGG_GAPS_SET_NULL);
+ PG_RETURN_NULL();
+ }
+ }
+ accumArrayResult(resultContent, RangeTypePGetDatum(lastRange), false, rangeTypeId, aggContext);
+ lastRange = currentRange;
+
+ }
+ else
+ {
+ /* They must overlap: */
+ if (state->flags & RANGE_AGG_OVERLAPS_FLAGS)
+ {
+ if (state->flags & RANGE_AGG_OVERLAPS_RAISE)
+ {
+ r1Str = "lastRange"; r2Str = "currentRange";
+ // TODO: Why is this segfaulting?:
+ // r1Str = DatumGetCString(DirectFunctionCall1(range_out, RangeTypePGetDatum(lastRange)));
+ // r2Str = DatumGetCString(DirectFunctionCall1(range_out, RangeTypePGetDatum(currentRange)));
+ ereport(ERROR, (errmsg("range_agg: overlap detected between %s and %s", r1Str, r2Str)));
+ }
+ else
+ {
+ Assert(state->flags & RANGE_AGG_OVERLAPS_SET_NULL);
+ PG_RETURN_NULL();
+ }
+ }
+ lastRange = range_union_internal(typcache, lastRange, currentRange, false);
+ }
+ }
+ accumArrayResult(resultContent, RangeTypePGetDatum(lastRange), false, rangeTypeId, aggContext);
+
+ if (type_is_array(get_fn_expr_rettype(fcinfo->flinfo)))
+ {
+ result = makeArrayResult(resultContent, CurrentMemoryContext);
+ PG_RETURN_DATUM(result);
+ }
+ else
+ {
+ PG_RETURN_DATUM(RangeTypePGetDatum(lastRange));
+ }
+}
+
/* Btree support */
/* btree comparator */
@@ -2488,3 +2685,33 @@ datum_write(Pointer ptr, Datum datum, bool typbyval, char typalign,
return ptr;
}
+
+/*
+ * Compare two ranges so we can qsort them.
+ */
+static int
+element_compare(const void *key1, const void *key2, void *arg)
+{
+ Datum *d1 = (Datum *)key1;
+ Datum *d2 = (Datum *)key2;
+ RangeType *r1 = DatumGetRangeTypeP(*d1);
+ RangeType *r2 = DatumGetRangeTypeP(*d2);
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2) cmp = 0;
+ else if (empty1) cmp = -1;
+ else if (empty2) cmp = 1;
+ else {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0) cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index 044695a046..1c8aec3977 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -544,6 +544,47 @@
{ aggfnoid => 'jsonb_object_agg', aggtransfn => 'jsonb_object_agg_transfn',
aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(int4range,bool)', aggtransfn => 'int4range_agg_transfn',
+ aggfinalfn => 'int4range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(int8range,bool)', aggtransfn => 'int8range_agg_transfn',
+ aggfinalfn => 'int8range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(numrange,bool)', aggtransfn => 'numrange_agg_transfn',
+ aggfinalfn => 'numrange_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(tsrange,bool)', aggtransfn => 'tsrange_agg_transfn',
+ aggfinalfn => 'tsrange_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(tstzrange,bool)', aggtransfn => 'tstzrange_agg_transfn',
+ aggfinalfn => 'tstzrange_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(daterange,bool)', aggtransfn => 'daterange_agg_transfn',
+ aggfinalfn => 'daterange_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(int4range,bool,bool)', aggtransfn => 'int4rangebb_agg_transfn',
+ aggfinalfn => 'int4rangebb_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(int8range,bool,bool)', aggtransfn => 'int8rangebb_agg_transfn',
+ aggfinalfn => 'int8rangebb_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(numrange,bool,bool)', aggtransfn => 'numrangebb_agg_transfn',
+ aggfinalfn => 'numrangebb_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(tsrange,bool,bool)', aggtransfn => 'tsrangebb_agg_transfn',
+ aggfinalfn => 'tsrangebb_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(tstzrange,bool,bool)', aggtransfn => 'tstzrangebb_agg_transfn',
+ aggfinalfn => 'tstzrangebb_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(daterange,bool,bool)', aggtransfn => 'daterangebb_agg_transfn',
+ aggfinalfn => 'daterangebb_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# ordered-set and hypothetical-set aggregates
{ aggfnoid => 'percentile_disc(float8,anyelement)', aggkind => 'o',
aggnumdirectargs => '1', aggtransfn => 'ordered_set_transition',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 87335248a0..249220c8a2 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9538,6 +9538,144 @@
{ oid => '3869',
proname => 'range_minus', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_minus' },
+{ oid => '8000', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8001', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anyrange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8002', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+
+# Making 2- and 3-param range_agg polymorphic is difficult
+# because it would take an anyrange and return an anyrange[],
+# which doesn't exist.
+# As a workaround we define separate functions for each built-in range type.
+# This is what causes the mess in src/test/regress/expected/opr_sanity.out.
+{ oid => '8003', descr => 'aggregate transition function',
+ proname => 'int4range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal int4range bool', prosrc => 'range_agg_transfn' },
+{ oid => '8004', descr => 'aggregate final function',
+ proname => 'int4range_agg_finalfn', proisstrict => 'f', prorettype => '_int4range',
+ proargtypes => 'internal int4range bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8005', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_int4range', proargtypes => 'int4range bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8006', descr => 'aggregate transition function',
+ proname => 'int8range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal int8range bool', prosrc => 'range_agg_transfn' },
+{ oid => '8007', descr => 'aggregate final function',
+ proname => 'int8range_agg_finalfn', proisstrict => 'f', prorettype => '_int8range',
+ proargtypes => 'internal int8range bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8008', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_int8range', proargtypes => 'int8range bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8009', descr => 'aggregate transition function',
+ proname => 'numrange_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal numrange bool', prosrc => 'range_agg_transfn' },
+{ oid => '8010', descr => 'aggregate final function',
+ proname => 'numrange_agg_finalfn', proisstrict => 'f', prorettype => '_numrange',
+ proargtypes => 'internal numrange bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8011', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_numrange', proargtypes => 'numrange bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8012', descr => 'aggregate transition function',
+ proname => 'tsrange_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal tsrange bool', prosrc => 'range_agg_transfn' },
+{ oid => '8013', descr => 'aggregate final function',
+ proname => 'tsrange_agg_finalfn', proisstrict => 'f', prorettype => '_tsrange',
+ proargtypes => 'internal tsrange bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8014', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_tsrange', proargtypes => 'tsrange bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8015', descr => 'aggregate transition function',
+ proname => 'tstzrange_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal tstzrange bool', prosrc => 'range_agg_transfn' },
+{ oid => '8016', descr => 'aggregate final function',
+ proname => 'tstzrange_agg_finalfn', proisstrict => 'f', prorettype => '_tstzrange',
+ proargtypes => 'internal tstzrange bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8017', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_tstzrange', proargtypes => 'tstzrange bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8018', descr => 'aggregate transition function',
+ proname => 'daterange_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal daterange bool', prosrc => 'range_agg_transfn' },
+{ oid => '8019', descr => 'aggregate final function',
+ proname => 'daterange_agg_finalfn', proisstrict => 'f', prorettype => '_daterange',
+ proargtypes => 'internal daterange bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8020', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_daterange', proargtypes => 'daterange bool',
+ prosrc => 'aggregate_dummy' },
+
+{ oid => '8021', descr => 'aggregate transition function',
+ proname => 'int4rangebb_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal int4range bool bool', prosrc => 'range_agg_transfn' },
+{ oid => '8022', descr => 'aggregate final function',
+ proname => 'int4rangebb_agg_finalfn', proisstrict => 'f', prorettype => '_int4range',
+ proargtypes => 'internal int4range bool bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8023', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_int4range', proargtypes => 'int4range bool bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8024', descr => 'aggregate transition function',
+ proname => 'int8rangebb_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal int8range bool bool', prosrc => 'range_agg_transfn' },
+{ oid => '8025', descr => 'aggregate final function',
+ proname => 'int8rangebb_agg_finalfn', proisstrict => 'f', prorettype => '_int8range',
+ proargtypes => 'internal int8range bool bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8026', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_int8range', proargtypes => 'int8range bool bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8027', descr => 'aggregate transition function',
+ proname => 'numrangebb_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal numrange bool bool', prosrc => 'range_agg_transfn' },
+{ oid => '8028', descr => 'aggregate final function',
+ proname => 'numrangebb_agg_finalfn', proisstrict => 'f', prorettype => '_numrange',
+ proargtypes => 'internal numrange bool bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8029', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_numrange', proargtypes => 'numrange bool bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8030', descr => 'aggregate transition function',
+ proname => 'tsrangebb_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal tsrange bool bool', prosrc => 'range_agg_transfn' },
+{ oid => '8031', descr => 'aggregate final function',
+ proname => 'tsrangebb_agg_finalfn', proisstrict => 'f', prorettype => '_tsrange',
+ proargtypes => 'internal tsrange bool bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8032', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_tsrange', proargtypes => 'tsrange bool bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8033', descr => 'aggregate transition function',
+ proname => 'tstzrangebb_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal tstzrange bool bool', prosrc => 'range_agg_transfn' },
+{ oid => '8034', descr => 'aggregate final function',
+ proname => 'tstzrangebb_agg_finalfn', proisstrict => 'f', prorettype => '_tstzrange',
+ proargtypes => 'internal tstzrange bool bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8035', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_tstzrange', proargtypes => 'tstzrange bool bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8036', descr => 'aggregate transition function',
+ proname => 'daterangebb_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal daterange bool bool', prosrc => 'range_agg_transfn' },
+{ oid => '8037', descr => 'aggregate final function',
+ proname => 'daterangebb_agg_finalfn', proisstrict => 'f', prorettype => '_daterange',
+ proargtypes => 'internal daterange bool bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8038', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_daterange', proargtypes => 'daterange bool bool',
+ prosrc => 'aggregate_dummy' },
+
{ oid => '3870', descr => 'less-equal-greater',
proname => 'range_cmp', prorettype => 'int4',
proargtypes => 'anyrange anyrange', prosrc => 'range_cmp' },
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 85af36ee5b..a2cd607b31 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -168,9 +168,105 @@ WHERE p1.oid < p2.oid AND
p1.proretset != p2.proretset OR
p1.provolatile != p2.provolatile OR
p1.pronargs != p2.pronargs);
- oid | proname | oid | proname
------+---------+-----+---------
-(0 rows)
+ oid | proname | oid | proname
+------+-----------------------+------+-------------------------
+ 8019 | daterange_agg_finalfn | 8028 | numrangebb_agg_finalfn
+ 8019 | daterange_agg_finalfn | 8037 | daterangebb_agg_finalfn
+ 8019 | daterange_agg_finalfn | 8034 | tstzrangebb_agg_finalfn
+ 8019 | daterange_agg_finalfn | 8031 | tsrangebb_agg_finalfn
+ 8019 | daterange_agg_finalfn | 8025 | int8rangebb_agg_finalfn
+ 8019 | daterange_agg_finalfn | 8022 | int4rangebb_agg_finalfn
+ 8016 | tstzrange_agg_finalfn | 8028 | numrangebb_agg_finalfn
+ 8016 | tstzrange_agg_finalfn | 8037 | daterangebb_agg_finalfn
+ 8016 | tstzrange_agg_finalfn | 8034 | tstzrangebb_agg_finalfn
+ 8016 | tstzrange_agg_finalfn | 8031 | tsrangebb_agg_finalfn
+ 8016 | tstzrange_agg_finalfn | 8025 | int8rangebb_agg_finalfn
+ 8016 | tstzrange_agg_finalfn | 8022 | int4rangebb_agg_finalfn
+ 8013 | tsrange_agg_finalfn | 8028 | numrangebb_agg_finalfn
+ 8013 | tsrange_agg_finalfn | 8037 | daterangebb_agg_finalfn
+ 8013 | tsrange_agg_finalfn | 8034 | tstzrangebb_agg_finalfn
+ 8013 | tsrange_agg_finalfn | 8031 | tsrangebb_agg_finalfn
+ 8013 | tsrange_agg_finalfn | 8025 | int8rangebb_agg_finalfn
+ 8013 | tsrange_agg_finalfn | 8022 | int4rangebb_agg_finalfn
+ 8010 | numrange_agg_finalfn | 8028 | numrangebb_agg_finalfn
+ 8010 | numrange_agg_finalfn | 8037 | daterangebb_agg_finalfn
+ 8010 | numrange_agg_finalfn | 8034 | tstzrangebb_agg_finalfn
+ 8010 | numrange_agg_finalfn | 8031 | tsrangebb_agg_finalfn
+ 8010 | numrange_agg_finalfn | 8025 | int8rangebb_agg_finalfn
+ 8010 | numrange_agg_finalfn | 8022 | int4rangebb_agg_finalfn
+ 8007 | int8range_agg_finalfn | 8028 | numrangebb_agg_finalfn
+ 8007 | int8range_agg_finalfn | 8037 | daterangebb_agg_finalfn
+ 8007 | int8range_agg_finalfn | 8034 | tstzrangebb_agg_finalfn
+ 8007 | int8range_agg_finalfn | 8031 | tsrangebb_agg_finalfn
+ 8007 | int8range_agg_finalfn | 8025 | int8rangebb_agg_finalfn
+ 8007 | int8range_agg_finalfn | 8022 | int4rangebb_agg_finalfn
+ 8004 | int4range_agg_finalfn | 8028 | numrangebb_agg_finalfn
+ 8004 | int4range_agg_finalfn | 8037 | daterangebb_agg_finalfn
+ 8004 | int4range_agg_finalfn | 8034 | tstzrangebb_agg_finalfn
+ 8004 | int4range_agg_finalfn | 8031 | tsrangebb_agg_finalfn
+ 8004 | int4range_agg_finalfn | 8025 | int8rangebb_agg_finalfn
+ 8004 | int4range_agg_finalfn | 8022 | int4rangebb_agg_finalfn
+ 8001 | range_agg_finalfn | 8028 | numrangebb_agg_finalfn
+ 8001 | range_agg_finalfn | 8037 | daterangebb_agg_finalfn
+ 8001 | range_agg_finalfn | 8034 | tstzrangebb_agg_finalfn
+ 8001 | range_agg_finalfn | 8031 | tsrangebb_agg_finalfn
+ 8001 | range_agg_finalfn | 8025 | int8rangebb_agg_finalfn
+ 8001 | range_agg_finalfn | 8022 | int4rangebb_agg_finalfn
+ 8001 | range_agg_finalfn | 8019 | daterange_agg_finalfn
+ 8001 | range_agg_finalfn | 8016 | tstzrange_agg_finalfn
+ 8001 | range_agg_finalfn | 8013 | tsrange_agg_finalfn
+ 8001 | range_agg_finalfn | 8010 | numrange_agg_finalfn
+ 8001 | range_agg_finalfn | 8007 | int8range_agg_finalfn
+ 8001 | range_agg_finalfn | 8004 | int4range_agg_finalfn
+ 8003 | int4range_agg_transfn | 8036 | daterangebb_agg_transfn
+ 8003 | int4range_agg_transfn | 8027 | numrangebb_agg_transfn
+ 8003 | int4range_agg_transfn | 8030 | tsrangebb_agg_transfn
+ 8003 | int4range_agg_transfn | 8021 | int4rangebb_agg_transfn
+ 8003 | int4range_agg_transfn | 8024 | int8rangebb_agg_transfn
+ 8003 | int4range_agg_transfn | 8033 | tstzrangebb_agg_transfn
+ 8018 | daterange_agg_transfn | 8036 | daterangebb_agg_transfn
+ 8018 | daterange_agg_transfn | 8027 | numrangebb_agg_transfn
+ 8018 | daterange_agg_transfn | 8030 | tsrangebb_agg_transfn
+ 8018 | daterange_agg_transfn | 8021 | int4rangebb_agg_transfn
+ 8018 | daterange_agg_transfn | 8024 | int8rangebb_agg_transfn
+ 8018 | daterange_agg_transfn | 8033 | tstzrangebb_agg_transfn
+ 8015 | tstzrange_agg_transfn | 8036 | daterangebb_agg_transfn
+ 8015 | tstzrange_agg_transfn | 8027 | numrangebb_agg_transfn
+ 8015 | tstzrange_agg_transfn | 8030 | tsrangebb_agg_transfn
+ 8015 | tstzrange_agg_transfn | 8021 | int4rangebb_agg_transfn
+ 8015 | tstzrange_agg_transfn | 8024 | int8rangebb_agg_transfn
+ 8015 | tstzrange_agg_transfn | 8033 | tstzrangebb_agg_transfn
+ 8006 | int8range_agg_transfn | 8036 | daterangebb_agg_transfn
+ 8006 | int8range_agg_transfn | 8027 | numrangebb_agg_transfn
+ 8006 | int8range_agg_transfn | 8030 | tsrangebb_agg_transfn
+ 8006 | int8range_agg_transfn | 8021 | int4rangebb_agg_transfn
+ 8006 | int8range_agg_transfn | 8024 | int8rangebb_agg_transfn
+ 8006 | int8range_agg_transfn | 8033 | tstzrangebb_agg_transfn
+ 8012 | tsrange_agg_transfn | 8036 | daterangebb_agg_transfn
+ 8012 | tsrange_agg_transfn | 8027 | numrangebb_agg_transfn
+ 8012 | tsrange_agg_transfn | 8030 | tsrangebb_agg_transfn
+ 8012 | tsrange_agg_transfn | 8021 | int4rangebb_agg_transfn
+ 8012 | tsrange_agg_transfn | 8024 | int8rangebb_agg_transfn
+ 8012 | tsrange_agg_transfn | 8033 | tstzrangebb_agg_transfn
+ 8000 | range_agg_transfn | 8036 | daterangebb_agg_transfn
+ 8000 | range_agg_transfn | 8027 | numrangebb_agg_transfn
+ 8000 | range_agg_transfn | 8030 | tsrangebb_agg_transfn
+ 8000 | range_agg_transfn | 8003 | int4range_agg_transfn
+ 8000 | range_agg_transfn | 8021 | int4rangebb_agg_transfn
+ 8000 | range_agg_transfn | 8018 | daterange_agg_transfn
+ 8000 | range_agg_transfn | 8015 | tstzrange_agg_transfn
+ 8000 | range_agg_transfn | 8024 | int8rangebb_agg_transfn
+ 8000 | range_agg_transfn | 8006 | int8range_agg_transfn
+ 8000 | range_agg_transfn | 8012 | tsrange_agg_transfn
+ 8000 | range_agg_transfn | 8009 | numrange_agg_transfn
+ 8000 | range_agg_transfn | 8033 | tstzrangebb_agg_transfn
+ 8009 | numrange_agg_transfn | 8036 | daterangebb_agg_transfn
+ 8009 | numrange_agg_transfn | 8027 | numrangebb_agg_transfn
+ 8009 | numrange_agg_transfn | 8030 | tsrangebb_agg_transfn
+ 8009 | numrange_agg_transfn | 8021 | int4rangebb_agg_transfn
+ 8009 | numrange_agg_transfn | 8024 | int8rangebb_agg_transfn
+ 8009 | numrange_agg_transfn | 8033 | tstzrangebb_agg_transfn
+(96 rows)
-- Look for uses of different type OIDs in the argument/result type fields
-- for different aliases of the same built-in function.
@@ -196,7 +292,28 @@ ORDER BY 1, 2;
------------+------------
25 | 1043
1114 | 1184
-(2 rows)
+ 3831 | 3905
+ 3831 | 3907
+ 3831 | 3909
+ 3831 | 3911
+ 3831 | 3913
+ 3831 | 3927
+ 3905 | 3907
+ 3905 | 3909
+ 3905 | 3911
+ 3905 | 3913
+ 3905 | 3927
+ 3907 | 3909
+ 3907 | 3911
+ 3907 | 3913
+ 3907 | 3927
+ 3909 | 3911
+ 3909 | 3913
+ 3909 | 3927
+ 3911 | 3913
+ 3911 | 3927
+ 3913 | 3927
+(23 rows)
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
FROM pg_proc AS p1, pg_proc AS p2
@@ -231,7 +348,28 @@ ORDER BY 1, 2;
23 | 28
1114 | 1184
1560 | 1562
-(3 rows)
+ 3831 | 3904
+ 3831 | 3906
+ 3831 | 3908
+ 3831 | 3910
+ 3831 | 3912
+ 3831 | 3926
+ 3904 | 3906
+ 3904 | 3908
+ 3904 | 3910
+ 3904 | 3912
+ 3904 | 3926
+ 3906 | 3908
+ 3906 | 3910
+ 3906 | 3912
+ 3906 | 3926
+ 3908 | 3910
+ 3908 | 3912
+ 3908 | 3926
+ 3910 | 3912
+ 3910 | 3926
+ 3912 | 3926
+(24 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2
@@ -1716,10 +1854,58 @@ WHERE p1.oid < p2.oid AND p1.proname = p2.proname AND
p1.prokind = 'a' AND p2.prokind = 'a' AND
array_dims(p1.proargtypes) != array_dims(p2.proargtypes)
ORDER BY 1;
- oid | oid
---------------+---------
- count("any") | count()
-(1 row)
+ oid | oid
+------------------------------+--------------------------------------
+ count("any") | count()
+ range_agg(anyrange) | range_agg(daterange,boolean,boolean)
+ range_agg(anyrange) | range_agg(tstzrange,boolean,boolean)
+ range_agg(anyrange) | range_agg(tsrange,boolean,boolean)
+ range_agg(anyrange) | range_agg(numrange,boolean,boolean)
+ range_agg(anyrange) | range_agg(int8range,boolean,boolean)
+ range_agg(anyrange) | range_agg(int4range,boolean,boolean)
+ range_agg(anyrange) | range_agg(daterange,boolean)
+ range_agg(anyrange) | range_agg(tstzrange,boolean)
+ range_agg(anyrange) | range_agg(tsrange,boolean)
+ range_agg(anyrange) | range_agg(numrange,boolean)
+ range_agg(anyrange) | range_agg(int8range,boolean)
+ range_agg(anyrange) | range_agg(int4range,boolean)
+ range_agg(int4range,boolean) | range_agg(daterange,boolean,boolean)
+ range_agg(int4range,boolean) | range_agg(tstzrange,boolean,boolean)
+ range_agg(int4range,boolean) | range_agg(tsrange,boolean,boolean)
+ range_agg(int4range,boolean) | range_agg(numrange,boolean,boolean)
+ range_agg(int4range,boolean) | range_agg(int8range,boolean,boolean)
+ range_agg(int4range,boolean) | range_agg(int4range,boolean,boolean)
+ range_agg(int8range,boolean) | range_agg(daterange,boolean,boolean)
+ range_agg(int8range,boolean) | range_agg(tstzrange,boolean,boolean)
+ range_agg(int8range,boolean) | range_agg(tsrange,boolean,boolean)
+ range_agg(int8range,boolean) | range_agg(numrange,boolean,boolean)
+ range_agg(int8range,boolean) | range_agg(int8range,boolean,boolean)
+ range_agg(int8range,boolean) | range_agg(int4range,boolean,boolean)
+ range_agg(numrange,boolean) | range_agg(daterange,boolean,boolean)
+ range_agg(numrange,boolean) | range_agg(tstzrange,boolean,boolean)
+ range_agg(numrange,boolean) | range_agg(tsrange,boolean,boolean)
+ range_agg(numrange,boolean) | range_agg(numrange,boolean,boolean)
+ range_agg(numrange,boolean) | range_agg(int8range,boolean,boolean)
+ range_agg(numrange,boolean) | range_agg(int4range,boolean,boolean)
+ range_agg(tsrange,boolean) | range_agg(daterange,boolean,boolean)
+ range_agg(tsrange,boolean) | range_agg(tstzrange,boolean,boolean)
+ range_agg(tsrange,boolean) | range_agg(tsrange,boolean,boolean)
+ range_agg(tsrange,boolean) | range_agg(numrange,boolean,boolean)
+ range_agg(tsrange,boolean) | range_agg(int8range,boolean,boolean)
+ range_agg(tsrange,boolean) | range_agg(int4range,boolean,boolean)
+ range_agg(tstzrange,boolean) | range_agg(daterange,boolean,boolean)
+ range_agg(tstzrange,boolean) | range_agg(tstzrange,boolean,boolean)
+ range_agg(tstzrange,boolean) | range_agg(tsrange,boolean,boolean)
+ range_agg(tstzrange,boolean) | range_agg(numrange,boolean,boolean)
+ range_agg(tstzrange,boolean) | range_agg(int8range,boolean,boolean)
+ range_agg(tstzrange,boolean) | range_agg(int4range,boolean,boolean)
+ range_agg(daterange,boolean) | range_agg(daterange,boolean,boolean)
+ range_agg(daterange,boolean) | range_agg(tstzrange,boolean,boolean)
+ range_agg(daterange,boolean) | range_agg(tsrange,boolean,boolean)
+ range_agg(daterange,boolean) | range_agg(numrange,boolean,boolean)
+ range_agg(daterange,boolean) | range_agg(int8range,boolean,boolean)
+ range_agg(daterange,boolean) | range_agg(int4range,boolean,boolean)
+(49 rows)
-- For the same reason, built-in aggregates with default arguments are no good.
SELECT oid, proname
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index accf1e0d9e..703bc41491 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -1407,3 +1407,281 @@ create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14'))
+;
+-- range_agg with 1 arg:
+-- Forbidding gaps and overlaps:
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id
+ORDER BY room_id;
+ERROR: range_agg: gap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id
+ORDER BY room_id;
+ERROR: range_agg: overlap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+-------------------------
+ 2 | [07-01-2018,07-03-2018)
+ 3 |
+ 4 |
+ 5 | [07-01-2018,07-03-2018)
+ 7 | [07-01-2018,07-14-2018)
+(5 rows)
+
+-- Obeying discrete base types:
+SELECT range_agg(r)
+FROM (VALUES
+ (int4range( 0, 9, '[]')),
+ (int4range(10, 19, '[]'))
+) t(r);
+ range_agg
+-----------
+ [0,20)
+(1 row)
+
+-- range_agg with 2 args:
+-- Forbidding gaps (and overlaps):
+SELECT room_id, range_agg(booked_during, false)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+ERROR: range_agg: gap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during, false)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+ERROR: range_agg: overlap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during, false)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 3 |
+ 5 | {"[07-01-2018,07-03-2018)"}
+ 4 |
+ 2 | {"[07-01-2018,07-03-2018)"}
+ 7 | {"[07-01-2018,07-14-2018)"}
+(5 rows)
+
+-- Permitting gaps (but forbidding overlaps):
+SELECT room_id, range_agg(booked_during, true)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+ room_id | range_agg
+---------+-------------------------------------------------------
+ 1 | {"[07-01-2018,07-14-2018)","[07-20-2018,07-22-2018)"}
+(1 row)
+
+SELECT room_id, range_agg(booked_during, true)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+ERROR: range_agg: overlap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during, true)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 3 |
+ 5 | {"[07-01-2018,07-03-2018)"}
+ 4 |
+ 2 | {"[07-01-2018,07-03-2018)"}
+ 7 | {"[07-01-2018,07-14-2018)"}
+(5 rows)
+
+-- range_agg with 3 args:
+-- Forbidding gaps and overlaps:
+SELECT room_id, range_agg(booked_during, false, false)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+ERROR: range_agg: gap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during, false, false)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+ERROR: range_agg: overlap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during, false, false)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 3 |
+ 5 | {"[07-01-2018,07-03-2018)"}
+ 4 |
+ 2 | {"[07-01-2018,07-03-2018)"}
+ 7 | {"[07-01-2018,07-14-2018)"}
+(5 rows)
+
+-- Forbidding gaps but permitting overlaps
+SELECT room_id, range_agg(booked_during, false, true)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+ERROR: range_agg: gap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during, false, true)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 6 | {"[07-01-2018,07-10-2018)"}
+(1 row)
+
+SELECT room_id, range_agg(booked_during, false, true)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 3 |
+ 5 | {"[07-01-2018,07-03-2018)"}
+ 4 |
+ 2 | {"[07-01-2018,07-03-2018)"}
+ 7 | {"[07-01-2018,07-14-2018)"}
+(5 rows)
+
+-- Permitting gaps but forbidding overlaps
+SELECT room_id, range_agg(booked_during, true, false)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+ room_id | range_agg
+---------+-------------------------------------------------------
+ 1 | {"[07-01-2018,07-14-2018)","[07-20-2018,07-22-2018)"}
+(1 row)
+
+SELECT room_id, range_agg(booked_during, true, false)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+ERROR: range_agg: overlap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during, true, false)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 3 |
+ 5 | {"[07-01-2018,07-03-2018)"}
+ 4 |
+ 2 | {"[07-01-2018,07-03-2018)"}
+ 7 | {"[07-01-2018,07-14-2018)"}
+(5 rows)
+
+-- Permitting gaps and overlaps:
+SELECT room_id, range_agg(booked_during, true, true)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+ room_id | range_agg
+---------+-------------------------------------------------------
+ 1 | {"[07-01-2018,07-14-2018)","[07-20-2018,07-22-2018)"}
+(1 row)
+
+SELECT room_id, range_agg(booked_during, true, true)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 6 | {"[07-01-2018,07-10-2018)"}
+(1 row)
+
+SELECT room_id, range_agg(booked_during, true, true)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 3 |
+ 5 | {"[07-01-2018,07-03-2018)"}
+ 4 |
+ 2 | {"[07-01-2018,07-03-2018)"}
+ 7 | {"[07-01-2018,07-14-2018)"}
+(5 rows)
+
+-- Obeying discrete base types:
+SELECT range_agg(r, false, false)
+FROM (VALUES
+ (int4range( 0, 5, '[]')),
+ (int4range( 7, 9, '[]'))
+) t(r);
+ERROR: range_agg: gap detected between lastRange and currentRange
+SELECT range_agg(r, false, false)
+FROM (VALUES
+ (int4range( 0, 5, '[]')),
+ (int4range( 5, 9, '[]'))
+) t(r);
+ERROR: range_agg: overlap detected between lastRange and currentRange
+SELECT range_agg(r, true, true)
+FROM (VALUES
+ (int4range( 0, 9, '[]')),
+ (int4range(10, 15, '[]')),
+ (int4range(20, 26, '[]')),
+ (int4range(26, 29, '[]'))
+) t(r);
+ range_agg
+----------------------
+ {"[0,16)","[20,30)"}
+(1 row)
+
+-- It combines with UNNEST
+-- to implement the temporal database "coalesce" function
+-- (see Snodgrass 6.5.2):
+SELECT room_id, t2.booked_during
+FROM (
+ SELECT room_id, range_agg(booked_during, true, true) AS booked_during
+ FROM reservations
+ GROUP BY room_id
+ ) AS t1,
+ UNNEST(t1.booked_during) AS t2(booked_during)
+ORDER BY room_id, booked_during
+;
+ room_id | booked_during
+---------+-------------------------
+ 1 | [07-01-2018,07-14-2018)
+ 1 | [07-20-2018,07-22-2018)
+ 2 | [07-01-2018,07-03-2018)
+ 5 | [07-01-2018,07-03-2018)
+ 6 | [07-01-2018,07-10-2018)
+ 7 | [07-01-2018,07-14-2018)
+(6 rows)
+
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 392e8a4957..bf4558952c 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -169,6 +169,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 55638a85ee..9d1be6f3ca 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -503,3 +503,192 @@ create function inoutparam_fail(inout i anyelement, out r anyrange)
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14'))
+;
+
+-- range_agg with 1 arg:
+
+-- Forbidding gaps and overlaps:
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id
+ORDER BY room_id;
+
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id
+ORDER BY room_id;
+
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id
+ORDER BY room_id;
+
+-- Obeying discrete base types:
+SELECT range_agg(r)
+FROM (VALUES
+ (int4range( 0, 9, '[]')),
+ (int4range(10, 19, '[]'))
+) t(r);
+
+-- range_agg with 2 args:
+
+-- Forbidding gaps (and overlaps):
+SELECT room_id, range_agg(booked_during, false)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, false)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, false)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+
+-- Permitting gaps (but forbidding overlaps):
+SELECT room_id, range_agg(booked_during, true)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, true)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, true)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+
+-- range_agg with 3 args:
+
+-- Forbidding gaps and overlaps:
+SELECT room_id, range_agg(booked_during, false, false)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, false, false)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, false, false)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+
+-- Forbidding gaps but permitting overlaps
+SELECT room_id, range_agg(booked_during, false, true)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, false, true)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, false, true)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+
+-- Permitting gaps but forbidding overlaps
+SELECT room_id, range_agg(booked_during, true, false)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, true, false)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, true, false)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+
+-- Permitting gaps and overlaps:
+SELECT room_id, range_agg(booked_during, true, true)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, true, true)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, true, true)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+
+-- Obeying discrete base types:
+SELECT range_agg(r, false, false)
+FROM (VALUES
+ (int4range( 0, 5, '[]')),
+ (int4range( 7, 9, '[]'))
+) t(r);
+
+SELECT range_agg(r, false, false)
+FROM (VALUES
+ (int4range( 0, 5, '[]')),
+ (int4range( 5, 9, '[]'))
+) t(r);
+
+SELECT range_agg(r, true, true)
+FROM (VALUES
+ (int4range( 0, 9, '[]')),
+ (int4range(10, 15, '[]')),
+ (int4range(20, 26, '[]')),
+ (int4range(26, 29, '[]'))
+) t(r);
+
+-- It combines with UNNEST
+-- to implement the temporal database "coalesce" function
+-- (see Snodgrass 6.5.2):
+SELECT room_id, t2.booked_during
+FROM (
+ SELECT room_id, range_agg(booked_during, true, true) AS booked_during
+ FROM reservations
+ GROUP BY room_id
+ ) AS t1,
+ UNNEST(t1.booked_during) AS t2(booked_during)
+ORDER BY room_id, booked_during
+;
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-04 01:41 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 18:19 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-06 22:07 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 23:21 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-09 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-06-16 22:59 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-04 18:33 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2019-06-16 22:59 UTC (permalink / raw)
To: David Fetter <david@fetter.org>; +Cc: pgsql-hackers
On Wed, May 8, 2019 at 9:54 PM Paul A Jungwirth
<pj@illuminatedcomputing.com> wrote:
> Here is an initial patch. I'd love to have some feedback! :-)
Here is a v2 rebased off current master. No substantive changes, but
it does fix one trivial git conflict.
After talking with David in Ottawa and hearing a good use-case from
one other person for his proposed weighted_range_agg and
covering_range_agg, I think *will* add those to this patch, but if
anyone wants to offer feedback on my approach so far, I'd appreciate
that too.
Yours,
Paul
Attachments:
[application/octet-stream] range_agg_v0002.patch (49.2K, ../../CA+renyV+ER5HXjFmw=JnOfX_ZeMWjECA9VPjCQ+fF23QFVTYGA@mail.gmail.com/2-range_agg_v0002.patch)
download | inline diff:
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index e918133874..768d7fb0c0 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14514,6 +14514,11 @@ NULL baz</literallayout>(3 rows)</entry>
<function>lower_inf</function>, and <function>upper_inf</function>
functions all return false for an empty range.
</para>
+
+ <para>
+ See also <xref linkend="functions-aggregate"/> about the aggregate
+ function <function>range_agg</function> for use with ranges.
+ </para>
</sect1>
<sect1 id="functions-aggregate">
@@ -14832,6 +14837,76 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ any range type
+ </entry>
+ <entry>
+ same as argument type
+ </entry>
+ <entry>No</entry>
+ <entry>input ranges merged into a single range, or an error if there are any gaps or overlaps</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>,
+ <replaceable>permit_gaps</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ (any range type, <type>boolean</type>)
+ </entry>
+ <entry>
+ array of the same range type as argument type
+ </entry>
+ <entry>No</entry>
+ <entry>
+ input ranges merged into a list of non-touching ranges,
+ with gaps permitted, rejected, or yielding a NULL result
+ as the second argument is true, false, or NULL respectively
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>,
+ <replaceable>permit_gaps</replaceable>,
+ <replaceable>permit_overlaps</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ (any range type, <type>boolean</type>, <type>boolean</type>)
+ </entry>
+ <entry>
+ array of the same range type as argument type
+ </entry>
+ <entry>No</entry>
+ <entry>
+ input ranges merged into a list of non-touching ranges,
+ with gaps permitted, rejected, or yielding a NULL result
+ as the second argument is true, false, or NULL respectively,
+ and overlaps permitted, rejected, or yielding a NULL result
+ as the third argument is true, false, or NULL respectively
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>string_agg</primary>
</indexterm>
<function>
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index cb16d701d8..bfa46bd389 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -34,6 +34,7 @@
#include "lib/stringinfo.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
+#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/hashutils.h"
@@ -54,6 +55,18 @@ typedef struct RangeIOData
FmgrInfo proc; /* lookup result for typiofunc */
} RangeIOData;
+/* aggregate state for range_agg */
+typedef struct RangeAggState
+{
+ ArrayBuildState *inputs;
+ uint8 flags;
+} RangeAggState;
+#define RANGE_AGG_GAPS_RAISE 0x01
+#define RANGE_AGG_GAPS_SET_NULL 0x02
+#define RANGE_AGG_OVERLAPS_RAISE 0x04
+#define RANGE_AGG_OVERLAPS_SET_NULL 0x08
+#define RANGE_AGG_GAPS_FLAGS (RANGE_AGG_GAPS_RAISE | RANGE_AGG_GAPS_SET_NULL)
+#define RANGE_AGG_OVERLAPS_FLAGS (RANGE_AGG_OVERLAPS_RAISE | RANGE_AGG_OVERLAPS_SET_NULL)
static RangeIOData *get_range_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
IOFuncSelector func);
@@ -69,6 +82,7 @@ static Size datum_compute_size(Size sz, Datum datum, bool typbyval,
char typalign, int16 typlen, char typstorage);
static Pointer datum_write(Pointer ptr, Datum datum, bool typbyval,
char typalign, int16 typlen, char typstorage);
+static int element_compare(const void *key1, const void *key2, void *arg);
/*
@@ -1135,6 +1149,189 @@ range_intersect(PG_FUNCTION_ARGS)
PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
}
+/* Aggregate functions */
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ * We can't simply use array_append as the transfn
+ * because we have to capture the second & third parameters
+ * and put them in the aggregate's running state,
+ * so that our finalfn can use them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ Oid rangeTypeId;
+ MemoryContext aggContext;
+ RangeAggState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ {
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+ }
+
+ rangeTypeId = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rangeTypeId))
+ {
+ ereport(ERROR, (errmsg("range_agg must be called with a range")));
+ }
+ if (PG_ARGISNULL(0))
+ {
+ state = MemoryContextAlloc(aggContext, sizeof(RangeAggState));
+ state->inputs = initArrayResult(rangeTypeId, aggContext, false);
+
+ state->flags = 0x0;
+ switch (PG_NARGS())
+ {
+ case 2:
+ state->flags |= RANGE_AGG_GAPS_RAISE;
+ state->flags |= RANGE_AGG_OVERLAPS_RAISE;
+ break;
+ case 3:
+ if (PG_ARGISNULL(2)) state->flags |= RANGE_AGG_GAPS_SET_NULL;
+ else if (!PG_GETARG_BOOL(2)) state->flags |= RANGE_AGG_GAPS_RAISE;
+ state->flags |= RANGE_AGG_OVERLAPS_RAISE;
+ break;
+ case 4:
+ if (PG_ARGISNULL(2)) state->flags |= RANGE_AGG_GAPS_SET_NULL;
+ else if (!PG_GETARG_BOOL(2)) state->flags |= RANGE_AGG_GAPS_RAISE;
+ if (PG_ARGISNULL(3)) state->flags |= RANGE_AGG_OVERLAPS_SET_NULL;
+ else if (!PG_GETARG_BOOL(3)) state->flags |= RANGE_AGG_OVERLAPS_RAISE;
+ break;
+ default:
+ Assert(false);
+ }
+ }
+ else
+ {
+ state = (RangeAggState *)PG_GETARG_POINTER(0);
+ }
+
+ /* Might as well skip NULLs here so the finalfn doesn't have to: */
+ if (!PG_ARGISNULL(1))
+ {
+ accumArrayResult(state->inputs, PG_GETARG_DATUM(1), false,
+ rangeTypeId, aggContext);
+ }
+
+ PG_RETURN_POINTER(state);
+}
+
+/* range_agg_finalfn: combine adjacent/overlapping ranges */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rangeTypeId;
+ TypeCacheEntry *typcache;
+ RangeAggState *state;
+ ArrayBuildState *inputArray;
+ int inputLength;
+ Datum *inputVals;
+ bool *inputNulls;
+ int i;
+ RangeType *currentRange;
+ RangeType *lastRange;
+ char *r1Str, *r2Str;
+ ArrayBuildState *resultContent;
+ Datum result;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ {
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+ }
+
+ state = PG_ARGISNULL(0) ? NULL : (RangeAggState *)PG_GETARG_POINTER(0);
+ if (state == NULL) PG_RETURN_NULL();
+ inputArray = state->inputs;
+ inputVals = inputArray->dvalues;
+ inputNulls = inputArray->dnulls;
+ inputLength = inputArray->nelems;
+ rangeTypeId = inputArray->element_type;
+
+ typcache = range_get_typcache(fcinfo, rangeTypeId);
+ if (inputLength == 0) PG_RETURN_NULL();
+ qsort_arg(inputVals, inputLength, sizeof(Datum), element_compare, typcache);
+
+ resultContent = initArrayResult(rangeTypeId, aggContext, false);
+ lastRange = DatumGetRangeTypeP(inputVals[0]);
+ for (i = 1; i < inputLength; i++)
+ {
+ Assert(!inputNulls[i]);
+ currentRange = DatumGetRangeTypeP(inputVals[i]);
+ /* range_adjacent_interval gives true
+ * if *either* A meets B or B meets A,
+ * which is not quite what we want,
+ * but we rely on the sorting above
+ * to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(typcache, lastRange, currentRange))
+ {
+ /* The two ranges touch without overlap: */
+ lastRange = range_union_internal(typcache, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(typcache, lastRange, currentRange))
+ {
+ /* There is a gap: */
+ if (state->flags & RANGE_AGG_GAPS_FLAGS)
+ {
+ if (state->flags & RANGE_AGG_GAPS_RAISE)
+ {
+ r1Str = "lastRange";
+ r2Str = "currentRange";
+ // TODO: Why is this segfaulting?:
+ // r1Str = DatumGetCString(DirectFunctionCall1(range_out, RangeTypePGetDatum(lastRange)));
+ // r2Str = DatumGetCString(DirectFunctionCall1(range_out, RangeTypePGetDatum(currentRange)));
+ ereport(ERROR, (errmsg("range_agg: gap detected between %s and %s", r1Str, r2Str)));
+ }
+ else
+ {
+ Assert(state->flags & RANGE_AGG_GAPS_SET_NULL);
+ PG_RETURN_NULL();
+ }
+ }
+ accumArrayResult(resultContent, RangeTypePGetDatum(lastRange), false, rangeTypeId, aggContext);
+ lastRange = currentRange;
+
+ }
+ else
+ {
+ /* They must overlap: */
+ if (state->flags & RANGE_AGG_OVERLAPS_FLAGS)
+ {
+ if (state->flags & RANGE_AGG_OVERLAPS_RAISE)
+ {
+ r1Str = "lastRange"; r2Str = "currentRange";
+ // TODO: Why is this segfaulting?:
+ // r1Str = DatumGetCString(DirectFunctionCall1(range_out, RangeTypePGetDatum(lastRange)));
+ // r2Str = DatumGetCString(DirectFunctionCall1(range_out, RangeTypePGetDatum(currentRange)));
+ ereport(ERROR, (errmsg("range_agg: overlap detected between %s and %s", r1Str, r2Str)));
+ }
+ else
+ {
+ Assert(state->flags & RANGE_AGG_OVERLAPS_SET_NULL);
+ PG_RETURN_NULL();
+ }
+ }
+ lastRange = range_union_internal(typcache, lastRange, currentRange, false);
+ }
+ }
+ accumArrayResult(resultContent, RangeTypePGetDatum(lastRange), false, rangeTypeId, aggContext);
+
+ if (type_is_array(get_fn_expr_rettype(fcinfo->flinfo)))
+ {
+ result = makeArrayResult(resultContent, CurrentMemoryContext);
+ PG_RETURN_DATUM(result);
+ }
+ else
+ {
+ PG_RETURN_DATUM(RangeTypePGetDatum(lastRange));
+ }
+}
+
/* Btree support */
/* btree comparator */
@@ -2488,3 +2685,33 @@ datum_write(Pointer ptr, Datum datum, bool typbyval, char typalign,
return ptr;
}
+
+/*
+ * Compare two ranges so we can qsort them.
+ */
+static int
+element_compare(const void *key1, const void *key2, void *arg)
+{
+ Datum *d1 = (Datum *)key1;
+ Datum *d2 = (Datum *)key2;
+ RangeType *r1 = DatumGetRangeTypeP(*d1);
+ RangeType *r2 = DatumGetRangeTypeP(*d2);
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2) cmp = 0;
+ else if (empty1) cmp = -1;
+ else if (empty2) cmp = 1;
+ else {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0) cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index 044695a046..1c8aec3977 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -544,6 +544,47 @@
{ aggfnoid => 'jsonb_object_agg', aggtransfn => 'jsonb_object_agg_transfn',
aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(int4range,bool)', aggtransfn => 'int4range_agg_transfn',
+ aggfinalfn => 'int4range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(int8range,bool)', aggtransfn => 'int8range_agg_transfn',
+ aggfinalfn => 'int8range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(numrange,bool)', aggtransfn => 'numrange_agg_transfn',
+ aggfinalfn => 'numrange_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(tsrange,bool)', aggtransfn => 'tsrange_agg_transfn',
+ aggfinalfn => 'tsrange_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(tstzrange,bool)', aggtransfn => 'tstzrange_agg_transfn',
+ aggfinalfn => 'tstzrange_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(daterange,bool)', aggtransfn => 'daterange_agg_transfn',
+ aggfinalfn => 'daterange_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(int4range,bool,bool)', aggtransfn => 'int4rangebb_agg_transfn',
+ aggfinalfn => 'int4rangebb_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(int8range,bool,bool)', aggtransfn => 'int8rangebb_agg_transfn',
+ aggfinalfn => 'int8rangebb_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(numrange,bool,bool)', aggtransfn => 'numrangebb_agg_transfn',
+ aggfinalfn => 'numrangebb_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(tsrange,bool,bool)', aggtransfn => 'tsrangebb_agg_transfn',
+ aggfinalfn => 'tsrangebb_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(tstzrange,bool,bool)', aggtransfn => 'tstzrangebb_agg_transfn',
+ aggfinalfn => 'tstzrangebb_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+{ aggfnoid => 'range_agg(daterange,bool,bool)', aggtransfn => 'daterangebb_agg_transfn',
+ aggfinalfn => 'daterangebb_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# ordered-set and hypothetical-set aggregates
{ aggfnoid => 'percentile_disc(float8,anyelement)', aggkind => 'o',
aggnumdirectargs => '1', aggtransfn => 'ordered_set_transition',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 87335248a0..249220c8a2 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9538,6 +9538,144 @@
{ oid => '3869',
proname => 'range_minus', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_minus' },
+{ oid => '8000', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8001', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anyrange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8002', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+
+# Making 2- and 3-param range_agg polymorphic is difficult
+# because it would take an anyrange and return an anyrange[],
+# which doesn't exist.
+# As a workaround we define separate functions for each built-in range type.
+# This is what causes the mess in src/test/regress/expected/opr_sanity.out.
+{ oid => '8003', descr => 'aggregate transition function',
+ proname => 'int4range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal int4range bool', prosrc => 'range_agg_transfn' },
+{ oid => '8004', descr => 'aggregate final function',
+ proname => 'int4range_agg_finalfn', proisstrict => 'f', prorettype => '_int4range',
+ proargtypes => 'internal int4range bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8005', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_int4range', proargtypes => 'int4range bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8006', descr => 'aggregate transition function',
+ proname => 'int8range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal int8range bool', prosrc => 'range_agg_transfn' },
+{ oid => '8007', descr => 'aggregate final function',
+ proname => 'int8range_agg_finalfn', proisstrict => 'f', prorettype => '_int8range',
+ proargtypes => 'internal int8range bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8008', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_int8range', proargtypes => 'int8range bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8009', descr => 'aggregate transition function',
+ proname => 'numrange_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal numrange bool', prosrc => 'range_agg_transfn' },
+{ oid => '8010', descr => 'aggregate final function',
+ proname => 'numrange_agg_finalfn', proisstrict => 'f', prorettype => '_numrange',
+ proargtypes => 'internal numrange bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8011', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_numrange', proargtypes => 'numrange bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8012', descr => 'aggregate transition function',
+ proname => 'tsrange_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal tsrange bool', prosrc => 'range_agg_transfn' },
+{ oid => '8013', descr => 'aggregate final function',
+ proname => 'tsrange_agg_finalfn', proisstrict => 'f', prorettype => '_tsrange',
+ proargtypes => 'internal tsrange bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8014', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_tsrange', proargtypes => 'tsrange bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8015', descr => 'aggregate transition function',
+ proname => 'tstzrange_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal tstzrange bool', prosrc => 'range_agg_transfn' },
+{ oid => '8016', descr => 'aggregate final function',
+ proname => 'tstzrange_agg_finalfn', proisstrict => 'f', prorettype => '_tstzrange',
+ proargtypes => 'internal tstzrange bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8017', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_tstzrange', proargtypes => 'tstzrange bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8018', descr => 'aggregate transition function',
+ proname => 'daterange_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal daterange bool', prosrc => 'range_agg_transfn' },
+{ oid => '8019', descr => 'aggregate final function',
+ proname => 'daterange_agg_finalfn', proisstrict => 'f', prorettype => '_daterange',
+ proargtypes => 'internal daterange bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8020', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_daterange', proargtypes => 'daterange bool',
+ prosrc => 'aggregate_dummy' },
+
+{ oid => '8021', descr => 'aggregate transition function',
+ proname => 'int4rangebb_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal int4range bool bool', prosrc => 'range_agg_transfn' },
+{ oid => '8022', descr => 'aggregate final function',
+ proname => 'int4rangebb_agg_finalfn', proisstrict => 'f', prorettype => '_int4range',
+ proargtypes => 'internal int4range bool bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8023', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_int4range', proargtypes => 'int4range bool bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8024', descr => 'aggregate transition function',
+ proname => 'int8rangebb_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal int8range bool bool', prosrc => 'range_agg_transfn' },
+{ oid => '8025', descr => 'aggregate final function',
+ proname => 'int8rangebb_agg_finalfn', proisstrict => 'f', prorettype => '_int8range',
+ proargtypes => 'internal int8range bool bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8026', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_int8range', proargtypes => 'int8range bool bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8027', descr => 'aggregate transition function',
+ proname => 'numrangebb_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal numrange bool bool', prosrc => 'range_agg_transfn' },
+{ oid => '8028', descr => 'aggregate final function',
+ proname => 'numrangebb_agg_finalfn', proisstrict => 'f', prorettype => '_numrange',
+ proargtypes => 'internal numrange bool bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8029', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_numrange', proargtypes => 'numrange bool bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8030', descr => 'aggregate transition function',
+ proname => 'tsrangebb_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal tsrange bool bool', prosrc => 'range_agg_transfn' },
+{ oid => '8031', descr => 'aggregate final function',
+ proname => 'tsrangebb_agg_finalfn', proisstrict => 'f', prorettype => '_tsrange',
+ proargtypes => 'internal tsrange bool bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8032', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_tsrange', proargtypes => 'tsrange bool bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8033', descr => 'aggregate transition function',
+ proname => 'tstzrangebb_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal tstzrange bool bool', prosrc => 'range_agg_transfn' },
+{ oid => '8034', descr => 'aggregate final function',
+ proname => 'tstzrangebb_agg_finalfn', proisstrict => 'f', prorettype => '_tstzrange',
+ proargtypes => 'internal tstzrange bool bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8035', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_tstzrange', proargtypes => 'tstzrange bool bool',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8036', descr => 'aggregate transition function',
+ proname => 'daterangebb_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal daterange bool bool', prosrc => 'range_agg_transfn' },
+{ oid => '8037', descr => 'aggregate final function',
+ proname => 'daterangebb_agg_finalfn', proisstrict => 'f', prorettype => '_daterange',
+ proargtypes => 'internal daterange bool bool', prosrc => 'range_agg_finalfn' },
+{ oid => '8038', descr => 'combinate aggregate input into a range',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => '_daterange', proargtypes => 'daterange bool bool',
+ prosrc => 'aggregate_dummy' },
+
{ oid => '3870', descr => 'less-equal-greater',
proname => 'range_cmp', prorettype => 'int4',
proargtypes => 'anyrange anyrange', prosrc => 'range_cmp' },
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 85af36ee5b..a2cd607b31 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -168,9 +168,105 @@ WHERE p1.oid < p2.oid AND
p1.proretset != p2.proretset OR
p1.provolatile != p2.provolatile OR
p1.pronargs != p2.pronargs);
- oid | proname | oid | proname
------+---------+-----+---------
-(0 rows)
+ oid | proname | oid | proname
+------+-----------------------+------+-------------------------
+ 8019 | daterange_agg_finalfn | 8028 | numrangebb_agg_finalfn
+ 8019 | daterange_agg_finalfn | 8037 | daterangebb_agg_finalfn
+ 8019 | daterange_agg_finalfn | 8034 | tstzrangebb_agg_finalfn
+ 8019 | daterange_agg_finalfn | 8031 | tsrangebb_agg_finalfn
+ 8019 | daterange_agg_finalfn | 8025 | int8rangebb_agg_finalfn
+ 8019 | daterange_agg_finalfn | 8022 | int4rangebb_agg_finalfn
+ 8016 | tstzrange_agg_finalfn | 8028 | numrangebb_agg_finalfn
+ 8016 | tstzrange_agg_finalfn | 8037 | daterangebb_agg_finalfn
+ 8016 | tstzrange_agg_finalfn | 8034 | tstzrangebb_agg_finalfn
+ 8016 | tstzrange_agg_finalfn | 8031 | tsrangebb_agg_finalfn
+ 8016 | tstzrange_agg_finalfn | 8025 | int8rangebb_agg_finalfn
+ 8016 | tstzrange_agg_finalfn | 8022 | int4rangebb_agg_finalfn
+ 8013 | tsrange_agg_finalfn | 8028 | numrangebb_agg_finalfn
+ 8013 | tsrange_agg_finalfn | 8037 | daterangebb_agg_finalfn
+ 8013 | tsrange_agg_finalfn | 8034 | tstzrangebb_agg_finalfn
+ 8013 | tsrange_agg_finalfn | 8031 | tsrangebb_agg_finalfn
+ 8013 | tsrange_agg_finalfn | 8025 | int8rangebb_agg_finalfn
+ 8013 | tsrange_agg_finalfn | 8022 | int4rangebb_agg_finalfn
+ 8010 | numrange_agg_finalfn | 8028 | numrangebb_agg_finalfn
+ 8010 | numrange_agg_finalfn | 8037 | daterangebb_agg_finalfn
+ 8010 | numrange_agg_finalfn | 8034 | tstzrangebb_agg_finalfn
+ 8010 | numrange_agg_finalfn | 8031 | tsrangebb_agg_finalfn
+ 8010 | numrange_agg_finalfn | 8025 | int8rangebb_agg_finalfn
+ 8010 | numrange_agg_finalfn | 8022 | int4rangebb_agg_finalfn
+ 8007 | int8range_agg_finalfn | 8028 | numrangebb_agg_finalfn
+ 8007 | int8range_agg_finalfn | 8037 | daterangebb_agg_finalfn
+ 8007 | int8range_agg_finalfn | 8034 | tstzrangebb_agg_finalfn
+ 8007 | int8range_agg_finalfn | 8031 | tsrangebb_agg_finalfn
+ 8007 | int8range_agg_finalfn | 8025 | int8rangebb_agg_finalfn
+ 8007 | int8range_agg_finalfn | 8022 | int4rangebb_agg_finalfn
+ 8004 | int4range_agg_finalfn | 8028 | numrangebb_agg_finalfn
+ 8004 | int4range_agg_finalfn | 8037 | daterangebb_agg_finalfn
+ 8004 | int4range_agg_finalfn | 8034 | tstzrangebb_agg_finalfn
+ 8004 | int4range_agg_finalfn | 8031 | tsrangebb_agg_finalfn
+ 8004 | int4range_agg_finalfn | 8025 | int8rangebb_agg_finalfn
+ 8004 | int4range_agg_finalfn | 8022 | int4rangebb_agg_finalfn
+ 8001 | range_agg_finalfn | 8028 | numrangebb_agg_finalfn
+ 8001 | range_agg_finalfn | 8037 | daterangebb_agg_finalfn
+ 8001 | range_agg_finalfn | 8034 | tstzrangebb_agg_finalfn
+ 8001 | range_agg_finalfn | 8031 | tsrangebb_agg_finalfn
+ 8001 | range_agg_finalfn | 8025 | int8rangebb_agg_finalfn
+ 8001 | range_agg_finalfn | 8022 | int4rangebb_agg_finalfn
+ 8001 | range_agg_finalfn | 8019 | daterange_agg_finalfn
+ 8001 | range_agg_finalfn | 8016 | tstzrange_agg_finalfn
+ 8001 | range_agg_finalfn | 8013 | tsrange_agg_finalfn
+ 8001 | range_agg_finalfn | 8010 | numrange_agg_finalfn
+ 8001 | range_agg_finalfn | 8007 | int8range_agg_finalfn
+ 8001 | range_agg_finalfn | 8004 | int4range_agg_finalfn
+ 8003 | int4range_agg_transfn | 8036 | daterangebb_agg_transfn
+ 8003 | int4range_agg_transfn | 8027 | numrangebb_agg_transfn
+ 8003 | int4range_agg_transfn | 8030 | tsrangebb_agg_transfn
+ 8003 | int4range_agg_transfn | 8021 | int4rangebb_agg_transfn
+ 8003 | int4range_agg_transfn | 8024 | int8rangebb_agg_transfn
+ 8003 | int4range_agg_transfn | 8033 | tstzrangebb_agg_transfn
+ 8018 | daterange_agg_transfn | 8036 | daterangebb_agg_transfn
+ 8018 | daterange_agg_transfn | 8027 | numrangebb_agg_transfn
+ 8018 | daterange_agg_transfn | 8030 | tsrangebb_agg_transfn
+ 8018 | daterange_agg_transfn | 8021 | int4rangebb_agg_transfn
+ 8018 | daterange_agg_transfn | 8024 | int8rangebb_agg_transfn
+ 8018 | daterange_agg_transfn | 8033 | tstzrangebb_agg_transfn
+ 8015 | tstzrange_agg_transfn | 8036 | daterangebb_agg_transfn
+ 8015 | tstzrange_agg_transfn | 8027 | numrangebb_agg_transfn
+ 8015 | tstzrange_agg_transfn | 8030 | tsrangebb_agg_transfn
+ 8015 | tstzrange_agg_transfn | 8021 | int4rangebb_agg_transfn
+ 8015 | tstzrange_agg_transfn | 8024 | int8rangebb_agg_transfn
+ 8015 | tstzrange_agg_transfn | 8033 | tstzrangebb_agg_transfn
+ 8006 | int8range_agg_transfn | 8036 | daterangebb_agg_transfn
+ 8006 | int8range_agg_transfn | 8027 | numrangebb_agg_transfn
+ 8006 | int8range_agg_transfn | 8030 | tsrangebb_agg_transfn
+ 8006 | int8range_agg_transfn | 8021 | int4rangebb_agg_transfn
+ 8006 | int8range_agg_transfn | 8024 | int8rangebb_agg_transfn
+ 8006 | int8range_agg_transfn | 8033 | tstzrangebb_agg_transfn
+ 8012 | tsrange_agg_transfn | 8036 | daterangebb_agg_transfn
+ 8012 | tsrange_agg_transfn | 8027 | numrangebb_agg_transfn
+ 8012 | tsrange_agg_transfn | 8030 | tsrangebb_agg_transfn
+ 8012 | tsrange_agg_transfn | 8021 | int4rangebb_agg_transfn
+ 8012 | tsrange_agg_transfn | 8024 | int8rangebb_agg_transfn
+ 8012 | tsrange_agg_transfn | 8033 | tstzrangebb_agg_transfn
+ 8000 | range_agg_transfn | 8036 | daterangebb_agg_transfn
+ 8000 | range_agg_transfn | 8027 | numrangebb_agg_transfn
+ 8000 | range_agg_transfn | 8030 | tsrangebb_agg_transfn
+ 8000 | range_agg_transfn | 8003 | int4range_agg_transfn
+ 8000 | range_agg_transfn | 8021 | int4rangebb_agg_transfn
+ 8000 | range_agg_transfn | 8018 | daterange_agg_transfn
+ 8000 | range_agg_transfn | 8015 | tstzrange_agg_transfn
+ 8000 | range_agg_transfn | 8024 | int8rangebb_agg_transfn
+ 8000 | range_agg_transfn | 8006 | int8range_agg_transfn
+ 8000 | range_agg_transfn | 8012 | tsrange_agg_transfn
+ 8000 | range_agg_transfn | 8009 | numrange_agg_transfn
+ 8000 | range_agg_transfn | 8033 | tstzrangebb_agg_transfn
+ 8009 | numrange_agg_transfn | 8036 | daterangebb_agg_transfn
+ 8009 | numrange_agg_transfn | 8027 | numrangebb_agg_transfn
+ 8009 | numrange_agg_transfn | 8030 | tsrangebb_agg_transfn
+ 8009 | numrange_agg_transfn | 8021 | int4rangebb_agg_transfn
+ 8009 | numrange_agg_transfn | 8024 | int8rangebb_agg_transfn
+ 8009 | numrange_agg_transfn | 8033 | tstzrangebb_agg_transfn
+(96 rows)
-- Look for uses of different type OIDs in the argument/result type fields
-- for different aliases of the same built-in function.
@@ -196,7 +292,28 @@ ORDER BY 1, 2;
------------+------------
25 | 1043
1114 | 1184
-(2 rows)
+ 3831 | 3905
+ 3831 | 3907
+ 3831 | 3909
+ 3831 | 3911
+ 3831 | 3913
+ 3831 | 3927
+ 3905 | 3907
+ 3905 | 3909
+ 3905 | 3911
+ 3905 | 3913
+ 3905 | 3927
+ 3907 | 3909
+ 3907 | 3911
+ 3907 | 3913
+ 3907 | 3927
+ 3909 | 3911
+ 3909 | 3913
+ 3909 | 3927
+ 3911 | 3913
+ 3911 | 3927
+ 3913 | 3927
+(23 rows)
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
FROM pg_proc AS p1, pg_proc AS p2
@@ -231,7 +348,28 @@ ORDER BY 1, 2;
23 | 28
1114 | 1184
1560 | 1562
-(3 rows)
+ 3831 | 3904
+ 3831 | 3906
+ 3831 | 3908
+ 3831 | 3910
+ 3831 | 3912
+ 3831 | 3926
+ 3904 | 3906
+ 3904 | 3908
+ 3904 | 3910
+ 3904 | 3912
+ 3904 | 3926
+ 3906 | 3908
+ 3906 | 3910
+ 3906 | 3912
+ 3906 | 3926
+ 3908 | 3910
+ 3908 | 3912
+ 3908 | 3926
+ 3910 | 3912
+ 3910 | 3926
+ 3912 | 3926
+(24 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2
@@ -1716,10 +1854,58 @@ WHERE p1.oid < p2.oid AND p1.proname = p2.proname AND
p1.prokind = 'a' AND p2.prokind = 'a' AND
array_dims(p1.proargtypes) != array_dims(p2.proargtypes)
ORDER BY 1;
- oid | oid
---------------+---------
- count("any") | count()
-(1 row)
+ oid | oid
+------------------------------+--------------------------------------
+ count("any") | count()
+ range_agg(anyrange) | range_agg(daterange,boolean,boolean)
+ range_agg(anyrange) | range_agg(tstzrange,boolean,boolean)
+ range_agg(anyrange) | range_agg(tsrange,boolean,boolean)
+ range_agg(anyrange) | range_agg(numrange,boolean,boolean)
+ range_agg(anyrange) | range_agg(int8range,boolean,boolean)
+ range_agg(anyrange) | range_agg(int4range,boolean,boolean)
+ range_agg(anyrange) | range_agg(daterange,boolean)
+ range_agg(anyrange) | range_agg(tstzrange,boolean)
+ range_agg(anyrange) | range_agg(tsrange,boolean)
+ range_agg(anyrange) | range_agg(numrange,boolean)
+ range_agg(anyrange) | range_agg(int8range,boolean)
+ range_agg(anyrange) | range_agg(int4range,boolean)
+ range_agg(int4range,boolean) | range_agg(daterange,boolean,boolean)
+ range_agg(int4range,boolean) | range_agg(tstzrange,boolean,boolean)
+ range_agg(int4range,boolean) | range_agg(tsrange,boolean,boolean)
+ range_agg(int4range,boolean) | range_agg(numrange,boolean,boolean)
+ range_agg(int4range,boolean) | range_agg(int8range,boolean,boolean)
+ range_agg(int4range,boolean) | range_agg(int4range,boolean,boolean)
+ range_agg(int8range,boolean) | range_agg(daterange,boolean,boolean)
+ range_agg(int8range,boolean) | range_agg(tstzrange,boolean,boolean)
+ range_agg(int8range,boolean) | range_agg(tsrange,boolean,boolean)
+ range_agg(int8range,boolean) | range_agg(numrange,boolean,boolean)
+ range_agg(int8range,boolean) | range_agg(int8range,boolean,boolean)
+ range_agg(int8range,boolean) | range_agg(int4range,boolean,boolean)
+ range_agg(numrange,boolean) | range_agg(daterange,boolean,boolean)
+ range_agg(numrange,boolean) | range_agg(tstzrange,boolean,boolean)
+ range_agg(numrange,boolean) | range_agg(tsrange,boolean,boolean)
+ range_agg(numrange,boolean) | range_agg(numrange,boolean,boolean)
+ range_agg(numrange,boolean) | range_agg(int8range,boolean,boolean)
+ range_agg(numrange,boolean) | range_agg(int4range,boolean,boolean)
+ range_agg(tsrange,boolean) | range_agg(daterange,boolean,boolean)
+ range_agg(tsrange,boolean) | range_agg(tstzrange,boolean,boolean)
+ range_agg(tsrange,boolean) | range_agg(tsrange,boolean,boolean)
+ range_agg(tsrange,boolean) | range_agg(numrange,boolean,boolean)
+ range_agg(tsrange,boolean) | range_agg(int8range,boolean,boolean)
+ range_agg(tsrange,boolean) | range_agg(int4range,boolean,boolean)
+ range_agg(tstzrange,boolean) | range_agg(daterange,boolean,boolean)
+ range_agg(tstzrange,boolean) | range_agg(tstzrange,boolean,boolean)
+ range_agg(tstzrange,boolean) | range_agg(tsrange,boolean,boolean)
+ range_agg(tstzrange,boolean) | range_agg(numrange,boolean,boolean)
+ range_agg(tstzrange,boolean) | range_agg(int8range,boolean,boolean)
+ range_agg(tstzrange,boolean) | range_agg(int4range,boolean,boolean)
+ range_agg(daterange,boolean) | range_agg(daterange,boolean,boolean)
+ range_agg(daterange,boolean) | range_agg(tstzrange,boolean,boolean)
+ range_agg(daterange,boolean) | range_agg(tsrange,boolean,boolean)
+ range_agg(daterange,boolean) | range_agg(numrange,boolean,boolean)
+ range_agg(daterange,boolean) | range_agg(int8range,boolean,boolean)
+ range_agg(daterange,boolean) | range_agg(int4range,boolean,boolean)
+(49 rows)
-- For the same reason, built-in aggregates with default arguments are no good.
SELECT oid, proname
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index accf1e0d9e..703bc41491 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -1407,3 +1407,281 @@ create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14'))
+;
+-- range_agg with 1 arg:
+-- Forbidding gaps and overlaps:
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id
+ORDER BY room_id;
+ERROR: range_agg: gap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id
+ORDER BY room_id;
+ERROR: range_agg: overlap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+-------------------------
+ 2 | [07-01-2018,07-03-2018)
+ 3 |
+ 4 |
+ 5 | [07-01-2018,07-03-2018)
+ 7 | [07-01-2018,07-14-2018)
+(5 rows)
+
+-- Obeying discrete base types:
+SELECT range_agg(r)
+FROM (VALUES
+ (int4range( 0, 9, '[]')),
+ (int4range(10, 19, '[]'))
+) t(r);
+ range_agg
+-----------
+ [0,20)
+(1 row)
+
+-- range_agg with 2 args:
+-- Forbidding gaps (and overlaps):
+SELECT room_id, range_agg(booked_during, false)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+ERROR: range_agg: gap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during, false)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+ERROR: range_agg: overlap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during, false)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 3 |
+ 5 | {"[07-01-2018,07-03-2018)"}
+ 4 |
+ 2 | {"[07-01-2018,07-03-2018)"}
+ 7 | {"[07-01-2018,07-14-2018)"}
+(5 rows)
+
+-- Permitting gaps (but forbidding overlaps):
+SELECT room_id, range_agg(booked_during, true)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+ room_id | range_agg
+---------+-------------------------------------------------------
+ 1 | {"[07-01-2018,07-14-2018)","[07-20-2018,07-22-2018)"}
+(1 row)
+
+SELECT room_id, range_agg(booked_during, true)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+ERROR: range_agg: overlap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during, true)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 3 |
+ 5 | {"[07-01-2018,07-03-2018)"}
+ 4 |
+ 2 | {"[07-01-2018,07-03-2018)"}
+ 7 | {"[07-01-2018,07-14-2018)"}
+(5 rows)
+
+-- range_agg with 3 args:
+-- Forbidding gaps and overlaps:
+SELECT room_id, range_agg(booked_during, false, false)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+ERROR: range_agg: gap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during, false, false)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+ERROR: range_agg: overlap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during, false, false)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 3 |
+ 5 | {"[07-01-2018,07-03-2018)"}
+ 4 |
+ 2 | {"[07-01-2018,07-03-2018)"}
+ 7 | {"[07-01-2018,07-14-2018)"}
+(5 rows)
+
+-- Forbidding gaps but permitting overlaps
+SELECT room_id, range_agg(booked_during, false, true)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+ERROR: range_agg: gap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during, false, true)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 6 | {"[07-01-2018,07-10-2018)"}
+(1 row)
+
+SELECT room_id, range_agg(booked_during, false, true)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 3 |
+ 5 | {"[07-01-2018,07-03-2018)"}
+ 4 |
+ 2 | {"[07-01-2018,07-03-2018)"}
+ 7 | {"[07-01-2018,07-14-2018)"}
+(5 rows)
+
+-- Permitting gaps but forbidding overlaps
+SELECT room_id, range_agg(booked_during, true, false)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+ room_id | range_agg
+---------+-------------------------------------------------------
+ 1 | {"[07-01-2018,07-14-2018)","[07-20-2018,07-22-2018)"}
+(1 row)
+
+SELECT room_id, range_agg(booked_during, true, false)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+ERROR: range_agg: overlap detected between lastRange and currentRange
+SELECT room_id, range_agg(booked_during, true, false)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 3 |
+ 5 | {"[07-01-2018,07-03-2018)"}
+ 4 |
+ 2 | {"[07-01-2018,07-03-2018)"}
+ 7 | {"[07-01-2018,07-14-2018)"}
+(5 rows)
+
+-- Permitting gaps and overlaps:
+SELECT room_id, range_agg(booked_during, true, true)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+ room_id | range_agg
+---------+-------------------------------------------------------
+ 1 | {"[07-01-2018,07-14-2018)","[07-20-2018,07-22-2018)"}
+(1 row)
+
+SELECT room_id, range_agg(booked_during, true, true)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 6 | {"[07-01-2018,07-10-2018)"}
+(1 row)
+
+SELECT room_id, range_agg(booked_during, true, true)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+ room_id | range_agg
+---------+-----------------------------
+ 3 |
+ 5 | {"[07-01-2018,07-03-2018)"}
+ 4 |
+ 2 | {"[07-01-2018,07-03-2018)"}
+ 7 | {"[07-01-2018,07-14-2018)"}
+(5 rows)
+
+-- Obeying discrete base types:
+SELECT range_agg(r, false, false)
+FROM (VALUES
+ (int4range( 0, 5, '[]')),
+ (int4range( 7, 9, '[]'))
+) t(r);
+ERROR: range_agg: gap detected between lastRange and currentRange
+SELECT range_agg(r, false, false)
+FROM (VALUES
+ (int4range( 0, 5, '[]')),
+ (int4range( 5, 9, '[]'))
+) t(r);
+ERROR: range_agg: overlap detected between lastRange and currentRange
+SELECT range_agg(r, true, true)
+FROM (VALUES
+ (int4range( 0, 9, '[]')),
+ (int4range(10, 15, '[]')),
+ (int4range(20, 26, '[]')),
+ (int4range(26, 29, '[]'))
+) t(r);
+ range_agg
+----------------------
+ {"[0,16)","[20,30)"}
+(1 row)
+
+-- It combines with UNNEST
+-- to implement the temporal database "coalesce" function
+-- (see Snodgrass 6.5.2):
+SELECT room_id, t2.booked_during
+FROM (
+ SELECT room_id, range_agg(booked_during, true, true) AS booked_during
+ FROM reservations
+ GROUP BY room_id
+ ) AS t1,
+ UNNEST(t1.booked_during) AS t2(booked_during)
+ORDER BY room_id, booked_during
+;
+ room_id | booked_during
+---------+-------------------------
+ 1 | [07-01-2018,07-14-2018)
+ 1 | [07-20-2018,07-22-2018)
+ 2 | [07-01-2018,07-03-2018)
+ 5 | [07-01-2018,07-03-2018)
+ 6 | [07-01-2018,07-10-2018)
+ 7 | [07-01-2018,07-14-2018)
+(6 rows)
+
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 8ff0da185e..395bfeb75d 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -170,6 +170,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 55638a85ee..9d1be6f3ca 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -503,3 +503,192 @@ create function inoutparam_fail(inout i anyelement, out r anyrange)
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14'))
+;
+
+-- range_agg with 1 arg:
+
+-- Forbidding gaps and overlaps:
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id
+ORDER BY room_id;
+
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id
+ORDER BY room_id;
+
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id
+ORDER BY room_id;
+
+-- Obeying discrete base types:
+SELECT range_agg(r)
+FROM (VALUES
+ (int4range( 0, 9, '[]')),
+ (int4range(10, 19, '[]'))
+) t(r);
+
+-- range_agg with 2 args:
+
+-- Forbidding gaps (and overlaps):
+SELECT room_id, range_agg(booked_during, false)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, false)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, false)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+
+-- Permitting gaps (but forbidding overlaps):
+SELECT room_id, range_agg(booked_during, true)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, true)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, true)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+
+-- range_agg with 3 args:
+
+-- Forbidding gaps and overlaps:
+SELECT room_id, range_agg(booked_during, false, false)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, false, false)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, false, false)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+
+-- Forbidding gaps but permitting overlaps
+SELECT room_id, range_agg(booked_during, false, true)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, false, true)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, false, true)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+
+-- Permitting gaps but forbidding overlaps
+SELECT room_id, range_agg(booked_during, true, false)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, true, false)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, true, false)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+
+-- Permitting gaps and overlaps:
+SELECT room_id, range_agg(booked_during, true, true)
+FROM reservations
+WHERE room_id = 1
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, true, true)
+FROM reservations
+WHERE room_id = 6
+GROUP BY room_id;
+
+SELECT room_id, range_agg(booked_during, true, true)
+FROM reservations
+WHERE room_id NOT IN (1, 6)
+GROUP BY room_id;
+
+-- Obeying discrete base types:
+SELECT range_agg(r, false, false)
+FROM (VALUES
+ (int4range( 0, 5, '[]')),
+ (int4range( 7, 9, '[]'))
+) t(r);
+
+SELECT range_agg(r, false, false)
+FROM (VALUES
+ (int4range( 0, 5, '[]')),
+ (int4range( 5, 9, '[]'))
+) t(r);
+
+SELECT range_agg(r, true, true)
+FROM (VALUES
+ (int4range( 0, 9, '[]')),
+ (int4range(10, 15, '[]')),
+ (int4range(20, 26, '[]')),
+ (int4range(26, 29, '[]'))
+) t(r);
+
+-- It combines with UNNEST
+-- to implement the temporal database "coalesce" function
+-- (see Snodgrass 6.5.2):
+SELECT room_id, t2.booked_during
+FROM (
+ SELECT room_id, range_agg(booked_during, true, true) AS booked_during
+ FROM reservations
+ GROUP BY room_id
+ ) AS t1,
+ UNNEST(t1.booked_during) AS t2(booked_during)
+ORDER BY room_id, booked_during
+;
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-04 01:41 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 18:19 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-06 22:07 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 23:21 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-09 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-06-16 22:59 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-07-04 18:33 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2019-07-05 11:30 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-07-05 17:00 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 2 replies; 152+ messages in thread
From: Alvaro Herrera @ 2019-07-04 18:33 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: David Fetter <david@fetter.org>; pgsql-hackers
I noticed that this patch has a // comment about it segfaulting. Did
you ever figure that out? Is the resulting code the one you intend as
final?
Did you make any inroads regarding Jeff Davis' suggestion about
implementing "multiranges"? I wonder if that's going to end up being a
Pandora's box.
Stylistically, the code does not match pgindent's choices very closely.
I can return a diff to a pgindented version of your v0002 for your
perusal, if it would be useful for you to learn its style. (A committer
would eventually run pgindent himself[1], but it's good to have
submissions be at least close to the final form.) BTW, I suggest "git
format-patch -vN" to prepare files for submission.
[1] No female committers yet ... is this 2019?
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-04 01:41 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 18:19 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-06 22:07 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 23:21 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-09 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-06-16 22:59 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-04 18:33 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2019-07-05 11:30 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-07-05 17:22 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
1 sibling, 1 reply; 152+ messages in thread
From: Pavel Stehule @ 2019-07-05 11:30 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Paul A Jungwirth <pj@illuminatedcomputing.com>; David Fetter <david@fetter.org>; pgsql-hackers
čt 4. 7. 2019 v 20:34 odesílatel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:
> I noticed that this patch has a // comment about it segfaulting. Did
> you ever figure that out? Is the resulting code the one you intend as
> final?
>
> Did you make any inroads regarding Jeff Davis' suggestion about
> implementing "multiranges"? I wonder if that's going to end up being a
> Pandora's box.
Introduction of new datatype can be better, because we can ensure so data
are correctly ordered
> Stylistically, the code does not match pgindent's choices very closely.
> I can return a diff to a pgindented version of your v0002 for your
> perusal, if it would be useful for you to learn its style. (A committer
> would eventually run pgindent himself[1], but it's good to have
> submissions be at least close to the final form.) BTW, I suggest "git
> format-patch -vN" to prepare files for submission.
>
The first issue is unstable regress tests - there is a problem with
opr_sanity
SELECT p1.oid, p1.proname, p2.oid, p2.proname
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid < p2.oid AND
p1.prosrc = p2.prosrc AND
p1.prolang = 12 AND p2.prolang = 12 AND
(p1.prokind != 'a' OR p2.prokind != 'a') AND
(p1.prolang != p2.prolang OR
p1.prokind != p2.prokind OR
p1.prosecdef != p2.prosecdef OR
p1.proleakproof != p2.proleakproof OR
p1.proisstrict != p2.proisstrict OR
p1.proretset != p2.proretset OR
p1.provolatile != p2.provolatile OR
p1.pronargs != p2.pronargs)
ORDER BY p1.oid, p2.oid; -- requires explicit ORDER BY now
+ rangeTypeId = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rangeTypeId))
+ {
+ ereport(ERROR, (errmsg("range_agg must be called with a range")));
+ }
???
+ r1Str = "lastRange";
+ r2Str = "currentRange";
+ // TODO: Why is this segfaulting?:
+ // r1Str =
DatumGetCString(DirectFunctionCall1(range_out,
RangeTypePGetDatum(lastRange)));
+ // r2Str =
DatumGetCString(DirectFunctionCall1(range_out,
RangeTypePGetDatum(currentRange)));
+ ereport(ERROR, (errmsg("range_agg: gap detected between
%s and %s", r1Str, r2Str)));
+ }
???
The patch doesn't respect Postgres formatting
+# Making 2- and 3-param range_agg polymorphic is difficult
+# because it would take an anyrange and return an anyrange[],
+# which doesn't exist.
+# As a workaround we define separate functions for each built-in range
type.
+# This is what causes the mess in src/test/regress/expected/opr_sanity.out.
+{ oid => '8003', descr => 'aggregate transition function',
This is strange. Does it means so range_agg will not work with custom range
types?
I am not sure about implementation. I think so accumulating all ranges,
sorting and processing on the end can be memory and CPU expensive.
Regards
Pavel
>
> [1] No female committers yet ... is this 2019?
>
> --
> Álvaro Herrera https://www.2ndQuadrant.com/
> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>
>
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-04 01:41 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 18:19 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-06 22:07 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 23:21 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-09 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-06-16 22:59 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-04 18:33 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-07-05 11:30 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-07-05 17:22 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 06:33 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2019-07-05 17:22 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; David Fetter <david@fetter.org>; pgsql-hackers
On Fri, Jul 5, 2019 at 4:31 AM Pavel Stehule <pavel.stehule@gmail.com> wrote:
> The first issue is unstable regress tests - there is a problem with opr_sanity
I would prefer to avoid needing to add anything to opr_sanity really.
A multirange would let me achieve that I think. But otherwise I'll add
the ordering. Thanks!
> + rangeTypeId = get_fn_expr_argtype(fcinfo->flinfo, 1);
> + if (!type_is_range(rangeTypeId))
> + {
> + ereport(ERROR, (errmsg("range_agg must be called with a range")));
> + }
I think this was left over from my attempts to support user-defined
ranges. I believe I can remove it now.
> +# Making 2- and 3-param range_agg polymorphic is difficult
> +# because it would take an anyrange and return an anyrange[],
> +# which doesn't exist.
> +# As a workaround we define separate functions for each built-in range type.
> +# This is what causes the mess in src/test/regress/expected/opr_sanity.out.
> +{ oid => '8003', descr => 'aggregate transition function',
>
> This is strange. Does it means so range_agg will not work with custom range types?
You would have to define your own range_agg with your own custom type
in the signature. I gave instructions about that in my range_agg
extension (https://github.com/pjungwir/range_agg), but it's not as
easy with range_agg in core because I don't think you can define a
custom function that is backed by a built-in C function. At least I
couldn't get that to work.
The biggest argument for a multirange to me is that we could have
anymultirange, with similar rules as anyarray. That way I could have
`range_agg(r anyrange) RETURNS anymultirange`. [1] is a conversation
where I asked about doing this before multiranges were suggested. Also
I'm aware of your own recent work on polymorphic types at [2] but I
haven't read it closely enough to see if it would help me here. Do you
think it applies?
> I am not sure about implementation. I think so accumulating all ranges, sorting and processing on the end can be memory and CPU expensive.
I did some research and couldn't find any algorithm that was better
than O(n log n), but if you're aware of any I'd like to know about it.
Assuming we can't improve on the complexity bounds, I think a sort +
iteration is desirable because it keeps things simple and benefits
from past & future work on the sorting code. I care more about
optimizing time here than RAM, especially since we'll use this in FK
checks, where the inputs will rarely be more than a few and probably
never in the millions.
We especially want to avoid an O(n^2) algorithm, which would be easy
to stumble into if we naively accumulated the result as we go. For
example if we had multiranges you could imagine an implementation that
just did `result + r` for all inputs r. But that would have the same
n^2 pattern as looping over strcat.
We could use a tree to keep things sorted and accumulate as we go, but
the implementation would be more complicated and I think slower.
Thanks for the review!
Paul
[1] https://www.postgresql.org/message-id/CA%2BrenyVOjb4xQZGjdCnA54-1nzVSY%2B47-h4nkM-EP5J%3D1z%3Db9w%40...
[2] https://www.postgresql.org/message-id/CAFj8pRDna7VqNi8gR+Tt2Ktmz0cq5G93guc3Sbn_NVPLdXAkqA@mail.gmail...
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-04 01:41 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 18:19 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-06 22:07 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 23:21 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-09 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-06-16 22:59 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-04 18:33 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-07-05 11:30 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-07-05 17:22 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-07-06 06:33 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 152+ messages in thread
From: Pavel Stehule @ 2019-07-06 06:33 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; David Fetter <david@fetter.org>; pgsql-hackers
pá 5. 7. 2019 v 19:22 odesílatel Paul A Jungwirth <
pj@illuminatedcomputing.com> napsal:
> On Fri, Jul 5, 2019 at 4:31 AM Pavel Stehule <pavel.stehule@gmail.com>
> wrote:
> > The first issue is unstable regress tests - there is a problem with
> opr_sanity
>
> I would prefer to avoid needing to add anything to opr_sanity really.
> A multirange would let me achieve that I think. But otherwise I'll add
> the ordering. Thanks!
>
> > + rangeTypeId = get_fn_expr_argtype(fcinfo->flinfo, 1);
> > + if (!type_is_range(rangeTypeId))
> > + {
> > + ereport(ERROR, (errmsg("range_agg must be called with a
> range")));
> > + }
>
> I think this was left over from my attempts to support user-defined
> ranges. I believe I can remove it now.
>
> > +# Making 2- and 3-param range_agg polymorphic is difficult
> > +# because it would take an anyrange and return an anyrange[],
> > +# which doesn't exist.
> > +# As a workaround we define separate functions for each built-in range
> type.
> > +# This is what causes the mess in
> src/test/regress/expected/opr_sanity.out.
> > +{ oid => '8003', descr => 'aggregate transition function',
> >
> > This is strange. Does it means so range_agg will not work with custom
> range types?
>
> You would have to define your own range_agg with your own custom type
> in the signature. I gave instructions about that in my range_agg
> extension (https://github.com/pjungwir/range_agg), but it's not as
> easy with range_agg in core because I don't think you can define a
> custom function that is backed by a built-in C function. At least I
> couldn't get that to work.
>
I understand so anybody can implement own function, but this is fundamental
problem in implementation.
In this case I prefer to start implement anyrangearray type first. It is
not hard work.
>
> The biggest argument for a multirange to me is that we could have
> anymultirange, with similar rules as anyarray. That way I could have
> `range_agg(r anyrange) RETURNS anymultirange`. [1] is a conversation
> where I asked about doing this before multiranges were suggested. Also
> I'm aware of your own recent work on polymorphic types at [2] but I
> haven't read it closely enough to see if it would help me here. Do you
> think it applies?
>
I don't see any difference between anymultirange or anyrangearray - it is
just name
ANSI SQL has a special kind for this purpose - "set". It is maybe better,
because PostgreSQL's arrays revoke a idea of ordering, but it is hard to do
some generic order for ranges.
Functionality is important - if you can do some special functionality, that
cannot be implemented as "array of ranges", then the new type is necessary.
If all functionality can be covered by array of ranges, then there is not
necessity of new type.
I don't talk about polymorphic types - probably it needs one -
"anymultirange" or "anyrangearray".
If some operation can be done smarter with new type, then I am ok for new
type, if not, then we should to use array of ranges.
> > I am not sure about implementation. I think so accumulating all ranges,
> sorting and processing on the end can be memory and CPU expensive.
>
> I did some research and couldn't find any algorithm that was better
> than O(n log n), but if you're aware of any I'd like to know about it.
> Assuming we can't improve on the complexity bounds, I think a sort +
> iteration is desirable because it keeps things simple and benefits
> from past & future work on the sorting code. I care more about
> optimizing time here than RAM, especially since we'll use this in FK
> checks, where the inputs will rarely be more than a few and probably
> never in the millions.
>
> We especially want to avoid an O(n^2) algorithm, which would be easy
> to stumble into if we naively accumulated the result as we go. For
> example if we had multiranges you could imagine an implementation that
> just did `result + r` for all inputs r. But that would have the same
> n^2 pattern as looping over strcat.
>
> We could use a tree to keep things sorted and accumulate as we go, but
> the implementation would be more complicated and I think slower.
>
I don't think - working with large arrays is slow, due often cache miss. I
understand so it depends on data, but in this area, I can imagine
significant memory reduction based on running processing.
array builder doesn't respect work_men, and I think so any different way is
safer.
Regards
Pavel
>
> Thanks for the review!
>
> Paul
>
> [1]
> https://www.postgresql.org/message-id/CA%2BrenyVOjb4xQZGjdCnA54-1nzVSY%2B47-h4nkM-EP5J%3D1z%3Db9w%40...
>
> [2]
> https://www.postgresql.org/message-id/CAFj8pRDna7VqNi8gR+Tt2Ktmz0cq5G93guc3Sbn_NVPLdXAkqA@mail.gmail...
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-04 01:41 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 18:19 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-06 22:07 ` Re: range_agg David Fetter <david@fetter.org>
2019-05-06 23:21 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-09 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-06-16 22:59 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-04 18:33 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2019-07-05 17:00 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
1 sibling, 0 replies; 152+ messages in thread
From: Paul A Jungwirth @ 2019-07-05 17:00 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: David Fetter <david@fetter.org>; pgsql-hackers
On Thu, Jul 4, 2019 at 11:34 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
>
> I noticed that this patch has a // comment about it segfaulting. Did
> you ever figure that out? Is the resulting code the one you intend as
> final?
Thanks for the review! I haven't revisited it but I'll see if I can
track it down. I consider this a WIP patch, not something final. (I
don't think Postgres likes C++-style comments, so anything that is //
marks something I consider needs more work.)
> Stylistically, the code does not match pgindent's choices very closely.
> I can return a diff to a pgindented version of your v0002 for your
> perusal, if it would be useful for you to learn its style.
Sorry about that, and thank you for making it easier for me to learn
how to do it the right way. :-)
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
@ 2019-05-04 22:11 ` Corey Huinker <corey.huinker@gmail.com>
2019-05-06 18:17 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
3 siblings, 1 reply; 152+ messages in thread
From: Corey Huinker @ 2019-05-04 22:11 UTC (permalink / raw)
To: Paul Jungwirth <pj@illuminatedcomputing.com>; +Cc: pgsql-hackers
>
> One question is how to aggregate ranges that would leave gaps and/or
> overlaps. So in my extension there is a one-param version that forbids
> gaps & overlaps, but I let you permit them by passing extra parameters,
> so the signature is:
>
Perhaps a third way would be to allow and preserve the gaps.
A while back I wrote an extension called disjoint_date_range for storing
sets of dates where it was assumed that most dates would be contiguous. The
basic idea was that The core datatype was an array of ranges of dates, and
with every modification you'd unnest them all to their discrete elements
and use a window function to identify "runs" of dates and recompose them
into a canonical set. It was an efficient way of representing "Every day
last year except for June 2nd and August 4th, when we closed business for
special events."
For arrays of ranges the principle is the same but it'd get a bit more
tricky, you'd have to order by low bound, use window functions to detect
adjacency/overlap to identify your runs, and the generate the canonical
minimum set of ranges in your array.
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-04 22:11 ` Re: range_agg Corey Huinker <corey.huinker@gmail.com>
@ 2019-05-06 18:17 ` Paul Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 0 replies; 152+ messages in thread
From: Paul Jungwirth @ 2019-05-06 18:17 UTC (permalink / raw)
To: Corey Huinker <corey.huinker@gmail.com>; +Cc: pgsql-hackers
On 5/4/19 3:11 PM, Corey Huinker wrote:
> One question is how to aggregate ranges that would leave gaps and/or
> overlaps. So in my extension there is a one-param version that forbids
> gaps & overlaps, but I let you permit them by passing extra parameters,
> so the signature is:
>
>
> Perhaps a third way would be to allow and preserve the gaps.
Thanks for the feedback! I think this is what I'm doing already
(returning an array of ranges), but let me know if I'm misunderstanding.
My extension has these signatures:
range_agg(anyrange) returning anyrange
range_agg(anyrange, bool) returning anyarray
range_agg(anyrange, bool, bool) returning anyarray.
The first variant raises an error if there are gaps or overlaps and
always returns a single range, but the other two return an array of ranges.
I was planning to use the same signatures for my patch to pg, unless
someone thinks they should be different. But I'm starting to wonder if
they shouldn't *all* return arrays. I have two concrete use-cases for
these functions and they both require the array-returning versions. Is
it helpful to have a version that always returns a single range? Or
should I make them all consistent?
Thanks,
--
Paul ~{:-)
pj@illuminatedcomputing.com
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
@ 2019-07-01 22:38 ` Jeff Davis <pgsql@j-davis.com>
2019-07-05 05:58 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-05 17:38 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
3 siblings, 3 replies; 152+ messages in thread
From: Jeff Davis @ 2019-07-01 22:38 UTC (permalink / raw)
To: Paul Jungwirth <pj@illuminatedcomputing.com>; pgsql-hackers
On Fri, 2019-05-03 at 15:56 -0700, Paul Jungwirth wrote:
> Hello,
>
> I wrote an extension to add a range_agg function with similar
> behavior
> to existing *_agg functions, and I'm wondering if folks would like
> to
> have it in core? Here is the repo:
> https://github.com/pjungwir/range_agg
This seems like a very useful extension, thank you.
For getting into core though, it should be a more complete set of
related operations. The patch is implicitly introducing the concept of
a "multirange" (in this case, an array of ranges), but it's not making
the concept whole.
What else should return a multirange? This patch implements the union-
agg of ranges, but we also might want an intersection-agg of ranges
(that is, the set of ranges that are subranges of every input). Given
that there are other options here, the name "range_agg" is too generic
to mean union-agg specifically.
What can we do with a multirange? A lot of range operators still make
sense, like "contains" or "overlaps"; but "adjacent" doesn't quite
work. What about new operations like inverting a multirange to get the
gaps?
Do we want to continue with the array-of-ranges implementation of a
multirange, or do we want a first-class multirange concept that might
eliminate the boilerplate around defining all of these operations?
If we have a more complete set of operators here, the flags for
handling overlapping ranges and gaps will be unnecessary.
Regards,
Jeff Davis
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
@ 2019-07-05 05:58 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-07-05 16:48 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2 siblings, 1 reply; 152+ messages in thread
From: Pavel Stehule @ 2019-07-05 05:58 UTC (permalink / raw)
To: Jeff Davis <pgsql@j-davis.com>; +Cc: Paul Jungwirth <pj@illuminatedcomputing.com>; pgsql-hackers
Hi
út 2. 7. 2019 v 0:38 odesílatel Jeff Davis <pgsql@j-davis.com> napsal:
> On Fri, 2019-05-03 at 15:56 -0700, Paul Jungwirth wrote:
> > Hello,
> >
> > I wrote an extension to add a range_agg function with similar
> > behavior
> > to existing *_agg functions, and I'm wondering if folks would like
> > to
> > have it in core? Here is the repo:
> > https://github.com/pjungwir/range_agg
>
> This seems like a very useful extension, thank you.
>
> For getting into core though, it should be a more complete set of
> related operations. The patch is implicitly introducing the concept of
> a "multirange" (in this case, an array of ranges), but it's not making
> the concept whole.
>
> What else should return a multirange? This patch implements the union-
> agg of ranges, but we also might want an intersection-agg of ranges
> (that is, the set of ranges that are subranges of every input). Given
> that there are other options here, the name "range_agg" is too generic
> to mean union-agg specifically.
>
> What can we do with a multirange? A lot of range operators still make
> sense, like "contains" or "overlaps"; but "adjacent" doesn't quite
> work. What about new operations like inverting a multirange to get the
> gaps?
>
> Do we want to continue with the array-of-ranges implementation of a
> multirange, or do we want a first-class multirange concept that might
> eliminate the boilerplate around defining all of these operations?
>
> If we have a more complete set of operators here, the flags for
> handling overlapping ranges and gaps will be unnecessary.
>
I think so scope of this patch is correct. Introduction of set of ranges
data type based on a array or not, should be different topic.
The question is naming - should be this agg function named "range_agg", and
multi range agg "multirange_agg"? Personally, I have not a problem with
range_agg, and I have not a problem so it is based on union operation. It
is true so only result of union can be implemented as range simply. When I
though about multi range result, then there are really large set of
possible algorithms how to do some operations over two, three values. So
personally, I am satisfied with scope of simple range_agg functions,
because I see a benefits, and I don't think so this implementation block
any more complex designs in the future. There is really big questions how
to implement multi range, and now I think so special data type will be
better than possible unordered arrays.
Regards
Pavel
Regards,
> Jeff Davis
>
>
>
>
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 05:58 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-07-05 16:48 ` Jeff Davis <pgsql@j-davis.com>
2019-07-06 06:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 152+ messages in thread
From: Jeff Davis @ 2019-07-05 16:48 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Paul Jungwirth <pj@illuminatedcomputing.com>; pgsql-hackers
On Fri, 2019-07-05 at 07:58 +0200, Pavel Stehule wrote:
> The question is naming - should be this agg function named
> "range_agg", and multi range agg "multirange_agg"? Personally, I have
> not a problem with range_agg, and I have not a problem so it is based
> on union operation. It is true so only result of union can be
> implemented as range simply. When I though about multi range result,
> then there are really large set of possible algorithms how to do some
> operations over two, three values.
Hi Pavel,
Can you explain in more detail? Would an intersection-based aggregate
be useful? If so, and we implement it in the future, what would we call
it?
Regards,
Jeff Davis
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 05:58 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-07-05 16:48 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
@ 2019-07-06 06:09 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 152+ messages in thread
From: Pavel Stehule @ 2019-07-06 06:09 UTC (permalink / raw)
To: Jeff Davis <pgsql@j-davis.com>; +Cc: Paul Jungwirth <pj@illuminatedcomputing.com>; pgsql-hackers
Hi
pá 5. 7. 2019 v 18:48 odesílatel Jeff Davis <pgsql@j-davis.com> napsal:
> On Fri, 2019-07-05 at 07:58 +0200, Pavel Stehule wrote:
> > The question is naming - should be this agg function named
> > "range_agg", and multi range agg "multirange_agg"? Personally, I have
> > not a problem with range_agg, and I have not a problem so it is based
> > on union operation. It is true so only result of union can be
> > implemented as range simply. When I though about multi range result,
> > then there are really large set of possible algorithms how to do some
> > operations over two, three values.
>
> Hi Pavel,
>
> Can you explain in more detail? Would an intersection-based aggregate
> be useful? If so, and we implement it in the future, what would we call
> it?
>
Intersection can be interesting - you can use it for planning - "is there a
intersection of free time ranges?"
About naming - what range_union_agg(), range_intersect_agg() ?
Regards
Pavel
>
> Regards,
> Jeff Davis
>
>
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
@ 2019-07-05 16:58 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-05 17:45 ` Re: range_agg David Fetter <david@fetter.org>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2 siblings, 2 replies; 152+ messages in thread
From: Paul A Jungwirth @ 2019-07-05 16:58 UTC (permalink / raw)
To: Jeff Davis <pgsql@j-davis.com>; +Cc: pgsql-hackers
On Mon, Jul 1, 2019 at 3:38 PM Jeff Davis <pgsql@j-davis.com> wrote:
>
> For getting into core though, it should be a more complete set of
> related operations. The patch is implicitly introducing the concept of
> a "multirange" (in this case, an array of ranges), but it's not making
> the concept whole.
>
> What else should return a multirange? This patch implements the union-
> agg of ranges, but we also might want an intersection-agg of ranges
> (that is, the set of ranges that are subranges of every input). Given
> that there are other options here, the name "range_agg" is too generic
> to mean union-agg specifically.
Thanks for the review!
I like the idea of adding a multirange type that works like range
types, although I'm not sure I want to build it. :-)
My own motivations for the range_agg patch are for temporal databases,
where I have two use-cases: checking temporal foreign keys [1] and
doing a Snodgrass "coalesce" operation [2]. The FKs use-case is more
important. For coalesce I just immediately UNNEST the array, so a
multirange would sort of "get in the way". It's no big deal if I can
cast a multirange to an array, although for something that would run
on every INSERT/UPDATE/DELETE I'd like to understand what the cast
would cost us in performance. But coalesce isn't part of SQL:2011 and
should be optional behavior or just something in an extension. The FKs
use case matters to me a lot more, and I think a multirange would be
fine for that. Also a multirange would solve the generics problem
Pavel asked about. (I'll say more about that in a reply to him.)
I'm not convinced that an intersection aggregate function for
multiranges would be used by anyone, but I don't mind building one.
Every other *_agg function has an "additive" sense, not a
"subtractive" sense. json{,b}_object_agg are the closest since you
*could* imagine intersection semantics for those, but they are unions.
So in terms of *naming* I think using "range_agg" for union semantics
is natural and would fit expectations. (I'm not the first to name this
function range_agg btw: [3]).
But there is clearly more than one worthwhile way to aggregate ranges:
- David suggested weighted_range_agg and covering_range_agg. At PGCon
2019 someone else said he has had to build something that was
essentially weighted_range_agg. I can see it used for
scheduling/capacity/max-utilization problems.
- You suggested an intersection range_agg.
- At [4] there is a missing_ranges function that only gives the *gaps*
between the input ranges.
Nonetheless I still think I would call the union behavior simply
range_agg, and then use weighted_range_agg, covering_range_agg,
intersection_range_agg, and missing_range_agg for the rest (assuming
we built them all). I'm not going to insist on any of that, but it's
what feels most user-friendly to me.
> What can we do with a multirange? A lot of range operators still make
> sense, like "contains" or "overlaps"; but "adjacent" doesn't quite
> work. What about new operations like inverting a multirange to get the
> gaps?
I can think of a lot of cool operators for `multirange \bigoplus
multirange` or `multirange \bigoplus range` (commuting of course). And
I've certainly wanted `range + range` and `range - range` in the past,
which would both return a multirange.
> If we have a more complete set of operators here, the flags for
> handling overlapping ranges and gaps will be unnecessary.
Both of my use cases should permit overlaps & gaps (range_agg(r, true,
true)), so I'm actually pretty okay with dropping the flags entirely
and just giving a one-param function that behavior. Or defining a
strict_range_agg that offers more control. But also I don't understand
how richer *operators* make the flags for the *aggregate* unnecessary.
So I really like the idea of multiranges, but I'm reluctant to take it
on myself, especially since this patch is just a utility function for
my other temporal patches. But I don't want my rush to leave a blemish
in our standard library either. But I think what really persuades me
to add multiranges is making a range_agg that more easily supports
user-defined range types. So how about I start on it and see how it
goes? I expect I can follow the existing code for range types pretty
closely, so maybe it won't be too hard.
Another option would be to rename my function range_array_agg (or
something) so that we are leaving space for a multirange function in
the future. I don't love this idea myself but it would could a Plan B.
What do you think of that?
Regards,
Paul
[1] With range_agg I can make the temporal FK check use similar SQL to
the non-temporal FK check:
SELECT 1
FROM [ONLY] <pktable> x
WHERE pkatt1 = $1 [AND ...]
FOR KEY SHARE OF x
vs
SELECT 1
FROM (
SELECT range_agg(r, true, true) AS r
FROM (
SELECT pkperiodatt AS r
FROM [ONLY] pktable x
WHERE pkatt1 = $1 [AND ...]
FOR KEY SHARE OF x
) x1
) x2
WHERE $n <@ x2.r[1]
(The temporal version could be simplified a bit if FOR KEY SHARE ever
supports aggregate functions.)
[2] page number 159ff in https://www2.cs.arizona.edu/~rts/tdbbook.pdf
(section 6.5.1, starts on page 183 of the PDF)
[3] https://git.proteus-tech.com/open-source/django-postgres/blob/fa91cf9b43ce942e84b1a9be22f445f3515ca3...
[4] https://schinckel.net/2014/11/18/aggregating-ranges-in-postgres/
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-07-05 17:45 ` David Fetter <david@fetter.org>
2019-07-05 17:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
1 sibling, 1 reply; 152+ messages in thread
From: David Fetter @ 2019-07-05 17:45 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Fri, Jul 05, 2019 at 09:58:02AM -0700, Paul A Jungwirth wrote:
> On Mon, Jul 1, 2019 at 3:38 PM Jeff Davis <pgsql@j-davis.com> wrote:
> >
> > For getting into core though, it should be a more complete set of
> > related operations. The patch is implicitly introducing the concept of
> > a "multirange" (in this case, an array of ranges), but it's not making
> > the concept whole.
> >
> > What else should return a multirange? This patch implements the union-
> > agg of ranges, but we also might want an intersection-agg of ranges
> > (that is, the set of ranges that are subranges of every input). Given
> > that there are other options here, the name "range_agg" is too generic
> > to mean union-agg specifically.
>
> Thanks for the review!
>
> I like the idea of adding a multirange type that works like range
> types, although I'm not sure I want to build it. :-)
>
> My own motivations for the range_agg patch are for temporal databases,
> where I have two use-cases: checking temporal foreign keys [1] and
> doing a Snodgrass "coalesce" operation [2]. The FKs use-case is more
> important. For coalesce I just immediately UNNEST the array, so a
> multirange would sort of "get in the way". It's no big deal if I can
> cast a multirange to an array, although for something that would run
> on every INSERT/UPDATE/DELETE I'd like to understand what the cast
> would cost us in performance. But coalesce isn't part of SQL:2011 and
> should be optional behavior or just something in an extension. The FKs
> use case matters to me a lot more, and I think a multirange would be
> fine for that. Also a multirange would solve the generics problem
> Pavel asked about. (I'll say more about that in a reply to him.)
>
> I'm not convinced that an intersection aggregate function for
> multiranges would be used by anyone, but I don't mind building one.
> Every other *_agg function has an "additive" sense, not a
> "subtractive" sense. json{,b}_object_agg are the closest since you
> *could* imagine intersection semantics for those, but they are unions.
> So in terms of *naming* I think using "range_agg" for union semantics
> is natural and would fit expectations. (I'm not the first to name this
> function range_agg btw: [3]).
>
> But there is clearly more than one worthwhile way to aggregate ranges:
>
> - David suggested weighted_range_agg and covering_range_agg. At PGCon
If I understand the cases correctly, the combination of covering_range
and multi_range types covers all cases. To recap, covering_range_agg
assigns a weight, possibly 0, to each non-overlapping sub-range. A
cast from covering_range to multi_range would meld adjacent ranges
with non-zero weights into single ranges in O(N) (N sub-ranges) time
and some teensy amount of memory for tracking progress of adjacent
ranges of non-zero weight. Gaps would just be multi_range consisting
of the sub-ranges of the covering_range with weight 0, and wouldn't
require any tracking.
What have I missed?
Best,
David.
--
David Fetter <david(at)fetter(dot)org> http://fetter.org/
Phone: +1 415 235 3778
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-05 17:45 ` Re: range_agg David Fetter <david@fetter.org>
@ 2019-07-05 17:57 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-05 17:59 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:26 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
0 siblings, 2 replies; 152+ messages in thread
From: Paul A Jungwirth @ 2019-07-05 17:57 UTC (permalink / raw)
To: David Fetter <david@fetter.org>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Fri, Jul 5, 2019 at 10:45 AM David Fetter <david@fetter.org> wrote:
> If I understand the cases correctly, the combination of covering_range
> and multi_range types covers all cases. To recap, covering_range_agg
> assigns a weight, possibly 0, to each non-overlapping sub-range. A
> cast from covering_range to multi_range would meld adjacent ranges
> with non-zero weights into single ranges in O(N) (N sub-ranges) time
> and some teensy amount of memory for tracking progress of adjacent
> ranges of non-zero weight. Gaps would just be multi_range consisting
> of the sub-ranges of the covering_range with weight 0, and wouldn't
> require any tracking.
I take it that a multirange contains of *disjoint* ranges, so instead
of {[1,2), [2,3), [6,7)} you'd have {[1,3), [6,7)}. Jeff does that
match your expectation?
I just realized that since weighted_range_agg and covering_range_agg
return tuples of (anyrange, integer) (maybe other numeric types too?),
their elements are *not ranges*, so they couldn't return a multirange.
They would have to return an array of those tuples.
I agree that if you had a pre-sorted list of weighted ranges (with or
without zero-weight elements), you could convert it to a multirange in
O(n) very easily.
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-05 17:45 ` Re: range_agg David Fetter <david@fetter.org>
2019-07-05 17:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-07-05 17:59 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
1 sibling, 0 replies; 152+ messages in thread
From: Paul A Jungwirth @ 2019-07-05 17:59 UTC (permalink / raw)
To: David Fetter <david@fetter.org>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Fri, Jul 5, 2019 at 10:57 AM Paul A Jungwirth
<pj@illuminatedcomputing.com> wrote:
> I take it that a multirange contains of *disjoint* ranges,
*consists* of. :-)
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-05 17:45 ` Re: range_agg David Fetter <david@fetter.org>
2019-07-05 17:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-07-06 19:26 ` Jeff Davis <pgsql@j-davis.com>
1 sibling, 0 replies; 152+ messages in thread
From: Jeff Davis @ 2019-07-06 19:26 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; David Fetter <david@fetter.org>; +Cc: pgsql-hackers
On Fri, 2019-07-05 at 10:57 -0700, Paul A Jungwirth wrote:
> I take it that a multirange contains of *disjoint* ranges, so instead
> of {[1,2), [2,3), [6,7)} you'd have {[1,3), [6,7)}. Jeff does that
> match your expectation?
Yes.
> I just realized that since weighted_range_agg and covering_range_agg
> return tuples of (anyrange, integer) (maybe other numeric types
> too?),
> their elements are *not ranges*, so they couldn't return a
> multirange.
> They would have to return an array of those tuples.
I think you are right. I was originally thinking a multirange and an
array of weights would work, but the multirange would coalesce adjacent
ranges because it would have no way to know they have different
weights.
Regards,
Jeff Davis
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-07-06 19:13 ` Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
1 sibling, 1 reply; 152+ messages in thread
From: Jeff Davis @ 2019-07-06 19:13 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: pgsql-hackers
On Fri, 2019-07-05 at 09:58 -0700, Paul A Jungwirth wrote:
> user-defined range types. So how about I start on it and see how it
> goes? I expect I can follow the existing code for range types pretty
> closely, so maybe it won't be too hard.
That would be great to at least take a look. If it starts to look like
a bad idea, then we can re-evaluate and see if it's better to just
return arrays.
The "weighted" version of the aggregate might be interesting... what
would it return exactly? An array of (range, weight) pairs, or an array
of ranges and an array of weights, or a multirange and an array of
weights?
> Another option would be to rename my function range_array_agg (or
> something) so that we are leaving space for a multirange function in
> the future. I don't love this idea myself but it would could a Plan
> B.
> What do you think of that?
Not excited about that either.
Regards,
Jeff Davis
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
@ 2019-07-08 16:46 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 05:08 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-07-09 15:51 ` Re: range_agg David Fetter <david@fetter.org>
2019-07-09 19:01 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-07-09 19:02 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 5 replies; 152+ messages in thread
From: Paul A Jungwirth @ 2019-07-08 16:46 UTC (permalink / raw)
To: Jeff Davis <pgsql@j-davis.com>; +Cc: pgsql-hackers
On Sat, Jul 6, 2019 at 12:13 PM Jeff Davis <pgsql@j-davis.com> wrote:
>
> On Fri, 2019-07-05 at 09:58 -0700, Paul A Jungwirth wrote:
> > user-defined range types. So how about I start on it and see how it
> > goes? I expect I can follow the existing code for range types pretty
> > closely, so maybe it won't be too hard.
>
> That would be great to at least take a look. If it starts to look like
> a bad idea, then we can re-evaluate and see if it's better to just
> return arrays.
I made some progress over the weekend. I don't have a patch yet but I
thought I'd ask for opinions on the approach I'm taking:
- A multirange type is an extra thing you get when you define a range
(just like how you get a tstzrange[]). Therefore....
- I don't need separate commands to add/drop multirange types. You get
one when you define a range type, and if you drop a range type it gets
dropped automatically.
- I'm adding a new typtype for multiranges. ('m' in pg_type).
- I'm just adding a mltrngtypeid column to pg_range. I don't think I
need a new pg_multirange table.
- You can have a multirange[].
- Multirange in/out work just like arrays, e.g. '{"[1,3)", "[5,6)"}'
- I'll add an anymultirange pseudotype. When it's the return type of a
function that has an "anyrange" parameter, it will use the same base
element type. (So basically anymultirange : anyrange :: anyarray ::
anyelement.)
- You can cast from a multirange to an array. The individual ranges
are always sorted in the result array.
- You can cast from an array to a multirange but it will error if
there are overlaps (or not?). The array's ranges don't have to be
sorted but they will be after a "round trip".
- Interesting functions:
- multirange_length
- range_agg (range_union_agg if you like)
- range_intersection_agg
- You can subscript a multirange like you do an array (? This could be
a function instead.)
- operators:
- union (++) and intersection (*):
- We already have + for range union but it raises an error if
there is a gap, so ++ is the same but with no errors.
- r ++ r = mr (commutative, associative)
- mr ++ r = mr
- r ++ mr = mr
- r * r = r (unchanged)
- mr * r = r
- r * mr = r
- mr - r = mr
- r - mr = mr
- mr - mr = mr
- comparison
- mr = mr
- mr @> x
- mr @> r
- mr @> mr
- x <@ mr
- r <@ mr
- mr <@ mr
- mr << mr (strictly left of)
- mr >> mr (strictly right of)
- mr &< mr (does not extend to the right of)
- mr &> mr (does not extend to the left of)
- inverse operator?:
- the inverse of {"[1,2)"} would be {"[null, 1)", "[2, null)"}.
- not sure we want this or what the symbol should be. I don't like
-mr as an inverse because then mr - mr != mr ++ -mr.
Anything in there you think should be different?
Thanks,
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-07-09 05:08 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-07-09 16:31 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 18:24 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
4 siblings, 2 replies; 152+ messages in thread
From: Pavel Stehule @ 2019-07-09 05:08 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
Hi
po 8. 7. 2019 v 18:47 odesílatel Paul A Jungwirth <
pj@illuminatedcomputing.com> napsal:
> On Sat, Jul 6, 2019 at 12:13 PM Jeff Davis <pgsql@j-davis.com> wrote:
> >
> > On Fri, 2019-07-05 at 09:58 -0700, Paul A Jungwirth wrote:
> > > user-defined range types. So how about I start on it and see how it
> > > goes? I expect I can follow the existing code for range types pretty
> > > closely, so maybe it won't be too hard.
> >
> > That would be great to at least take a look. If it starts to look like
> > a bad idea, then we can re-evaluate and see if it's better to just
> > return arrays.
>
> I made some progress over the weekend. I don't have a patch yet but I
> thought I'd ask for opinions on the approach I'm taking:
>
> - A multirange type is an extra thing you get when you define a range
> (just like how you get a tstzrange[]). Therefore....
>
I am not against a multirange type, but I miss a explanation why you
introduce new kind of types and don't use just array of ranges.
Introduction of new kind of types is not like introduction new type.
Regards
Pavel
- I don't need separate commands to add/drop multirange types. You get
> one when you define a range type, and if you drop a range type it gets
> dropped automatically.
> - I'm adding a new typtype for multiranges. ('m' in pg_type).
> - I'm just adding a mltrngtypeid column to pg_range. I don't think I
> need a new pg_multirange table.
> - You can have a multirange[].
> - Multirange in/out work just like arrays, e.g. '{"[1,3)", "[5,6)"}'
> - I'll add an anymultirange pseudotype. When it's the return type of a
> function that has an "anyrange" parameter, it will use the same base
> element type. (So basically anymultirange : anyrange :: anyarray ::
> anyelement.)
> - You can cast from a multirange to an array. The individual ranges
> are always sorted in the result array.
> - You can cast from an array to a multirange but it will error if
> there are overlaps (or not?). The array's ranges don't have to be
> sorted but they will be after a "round trip".
> - Interesting functions:
> - multirange_length
> - range_agg (range_union_agg if you like)
> - range_intersection_agg
> - You can subscript a multirange like you do an array (? This could be
> a function instead.)
> - operators:
> - union (++) and intersection (*):
> - We already have + for range union but it raises an error if
> there is a gap, so ++ is the same but with no errors.
> - r ++ r = mr (commutative, associative)
> - mr ++ r = mr
> - r ++ mr = mr
> - r * r = r (unchanged)
> - mr * r = r
> - r * mr = r
> - mr - r = mr
> - r - mr = mr
> - mr - mr = mr
> - comparison
> - mr = mr
> - mr @> x
> - mr @> r
> - mr @> mr
> - x <@ mr
> - r <@ mr
> - mr <@ mr
> - mr << mr (strictly left of)
> - mr >> mr (strictly right of)
> - mr &< mr (does not extend to the right of)
> - mr &> mr (does not extend to the left of)
> - inverse operator?:
> - the inverse of {"[1,2)"} would be {"[null, 1)", "[2, null)"}.
> - not sure we want this or what the symbol should be. I don't like
> -mr as an inverse because then mr - mr != mr ++ -mr.
>
> Anything in there you think should be different?
>
> Thanks,
> Paul
>
>
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 05:08 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-07-09 16:31 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
1 sibling, 0 replies; 152+ messages in thread
From: Paul A Jungwirth @ 2019-07-09 16:31 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Mon, Jul 8, 2019 at 10:09 PM Pavel Stehule <pavel.stehule@gmail.com> wrote:
> po 8. 7. 2019 v 18:47 odesílatel Paul A Jungwirth <pj@illuminatedcomputing.com> napsal:
> I am not against a multirange type, but I miss a explanation why you introduce new kind of types and don't use just array of ranges.
Hi Pavel, I'm sorry, and thanks for your feedback! I had a response to
your earlier email but it was stuck in my Drafts folder.
I do think a multirange would have enough new functionality to be
worth doing. I was pretty reluctant to take it on at first but the
idea is growing on me, and it does seem to offer a more sensible
interface. A lot of the value would come from range and multirange
operators, which we can't do with just arrays (I think). Having a
range get merged correctly when you add it would be very helpful. Also
it would be nice to have a data type that enforces a valid structure,
since not all range arrays are valid multiranges. My reply yesterday
to Jeff expands on this with all the functions/operators/etc we could
offer.
Your other email also asked:
> I don't think - working with large arrays is slow, due often cache miss.
>I understand so it depends on data, but in this area, I can imagine significant memory reduction based on running processing.
>
> array builder doesn't respect work_men, and I think so any different way is safer.
I'm still thinking about this one. I tried to work out how I'd
implement a tree-based sorted list of ranges so that I can quickly
insert/remove ranges. It is very complicated, and I started to feel
like I was just re-implementing GiST but in memory. I did find the
interval_set class from Boost's boost_icl library which could offer
some guidance. But for now I want to press forward with a
sort-then-iterate implementation and consider a different
implementation later. If you have any guidance I would appreciate it!
I especially want something that is O(n log n) to insert n ranges.
Other suggestions here are very welcome! :-)
Regards,
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 05:08 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-07-09 18:24 ` Jeff Davis <pgsql@j-davis.com>
2019-07-09 19:10 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 152+ messages in thread
From: Jeff Davis @ 2019-07-09 18:24 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: pgsql-hackers
On Tue, 2019-07-09 at 07:08 +0200, Pavel Stehule wrote:
>
> I am not against a multirange type, but I miss a explanation why you
> introduce new kind of types and don't use just array of ranges.
>
> Introduction of new kind of types is not like introduction new type.
The biggest benefit, in my opinion, is that it means you can define
functions/operators that take an "anyrange" and return an
"anymultirange". That way you don't have to define different functions
for int4 ranges, date ranges, etc.
It starts to get even more complex when you want to add opclasses, etc.
Ranges and arrays are effectively generic types that need a type
parameter to become a concrete type. Ideally, we'd have first-class
support for generic types, but I think that's a different topic ;-)
Regards,
Jeff Davis
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 05:08 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-07-09 18:24 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
@ 2019-07-09 19:10 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-07-09 19:23 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 152+ messages in thread
From: Pavel Stehule @ 2019-07-09 19:10 UTC (permalink / raw)
To: Jeff Davis <pgsql@j-davis.com>; +Cc: Paul A Jungwirth <pj@illuminatedcomputing.com>; pgsql-hackers
út 9. 7. 2019 v 20:25 odesílatel Jeff Davis <pgsql@j-davis.com> napsal:
> On Tue, 2019-07-09 at 07:08 +0200, Pavel Stehule wrote:
> >
> > I am not against a multirange type, but I miss a explanation why you
> > introduce new kind of types and don't use just array of ranges.
> >
> > Introduction of new kind of types is not like introduction new type.
>
> The biggest benefit, in my opinion, is that it means you can define
> functions/operators that take an "anyrange" and return an
> "anymultirange". That way you don't have to define different functions
> for int4 ranges, date ranges, etc.
>
>
I am not sure how strong is this argument.
I think so introduction of anyrangearray polymorphic type and enhancing
some type deduction can do same work.
It starts to get even more complex when you want to add opclasses, etc.
>
> Ranges and arrays are effectively generic types that need a type
> parameter to become a concrete type. Ideally, we'd have first-class
> support for generic types, but I think that's a different topic ;-)
I afraid so with generic multiragetype there lot of array infrastructure
will be duplicated
Regards
Pavel
> Regards,
> Jeff Davis
>
>
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 05:08 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-07-09 18:24 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-09 19:10 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-07-09 19:23 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-07-10 04:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Pavel Stehule @ 2019-07-09 19:23 UTC (permalink / raw)
To: Jeff Davis <pgsql@j-davis.com>; +Cc: Paul A Jungwirth <pj@illuminatedcomputing.com>; pgsql-hackers
út 9. 7. 2019 v 21:10 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
>
>
> út 9. 7. 2019 v 20:25 odesílatel Jeff Davis <pgsql@j-davis.com> napsal:
>
>> On Tue, 2019-07-09 at 07:08 +0200, Pavel Stehule wrote:
>> >
>> > I am not against a multirange type, but I miss a explanation why you
>> > introduce new kind of types and don't use just array of ranges.
>> >
>> > Introduction of new kind of types is not like introduction new type.
>>
>> The biggest benefit, in my opinion, is that it means you can define
>> functions/operators that take an "anyrange" and return an
>> "anymultirange". That way you don't have to define different functions
>> for int4 ranges, date ranges, etc.
>>
>>
> I am not sure how strong is this argument.
>
> I think so introduction of anyrangearray polymorphic type and enhancing
> some type deduction can do same work.
>
> It starts to get even more complex when you want to add opclasses, etc.
>>
>> Ranges and arrays are effectively generic types that need a type
>> parameter to become a concrete type. Ideally, we'd have first-class
>> support for generic types, but I think that's a different topic ;-)
>
>
> I afraid so with generic multiragetype there lot of array infrastructure
> will be duplicated
>
on second hand - it is true so classic array concat is not optimal for set
of ranges, so some functionality should be redefined every time.
I don't know what is possible, but for me - multiranges is special kind
(subset) of arrays and can be implement as subset of arrays. I remember
other possible kind of arrays - "sets" without duplicates. It is similar
case, I think.
Maybe introduction of multirages as new generic type is bad direction, and
can be better and more enhanceable in future to introduce some like special
kinds of arrays. So for example - unnest can be used directly for arrays
and multiranges too - because there will be common base.
Regards
Pavel
> Regards
>
> Pavel
>
>
>> Regards,
>> Jeff Davis
>>
>>
>>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 05:08 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-07-09 18:24 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-09 19:10 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-07-09 19:23 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-07-10 04:26 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-10 04:50 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2019-07-10 04:26 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Tue, Jul 9, 2019 at 12:24 PM Pavel Stehule <pavel.stehule@gmail.com> wrote:
> út 9. 7. 2019 v 21:10 odesílatel Pavel Stehule <pavel.stehule@gmail.com> napsal:
>> I afraid so with generic multiragetype there lot of array infrastructure will be duplicated
>
> on second hand - it is true so classic array concat is not optimal for set of ranges, so some functionality should be redefined every time.
>
> I don't know what is possible, but for me - multiranges is special kind (subset) of arrays and can be implement as subset of arrays. I remember other possible kind of arrays - "sets" without duplicates. It is similar case, I think.
>
> Maybe introduction of multirages as new generic type is bad direction, and can be better and more enhanceable in future to introduce some like special kinds of arrays. So for example - unnest can be used directly for arrays and multiranges too - because there will be common base.
Well I'm afraid of that too a bit, although I also agree it may be an
opportunity to share some common behavior and implementation. For
example in the discussion about string syntax, I think keeping it the
same as arrays is nicer for people and lets us share more between the
two types.
That said I don't think a multirange is a subtype of arrays (speaking
as a traditional object-oriented subtype), just something that shares
a lot of the same behavior. I'm inclined to maximize the overlap where
feasible though, e.g. string syntax, UNNEST, indexing, function naming
(`range_length`), etc. Something like Rust traits (or Java interfaces)
seems a closer mental model, not that we have to formalize that
somehow, particularly up front.
Yours,
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 05:08 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-07-09 18:24 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-09 19:10 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-07-09 19:23 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-07-10 04:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-07-10 04:50 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 152+ messages in thread
From: Pavel Stehule @ 2019-07-10 04:50 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
st 10. 7. 2019 v 6:26 odesílatel Paul A Jungwirth <
pj@illuminatedcomputing.com> napsal:
> On Tue, Jul 9, 2019 at 12:24 PM Pavel Stehule <pavel.stehule@gmail.com>
> wrote:
> > út 9. 7. 2019 v 21:10 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
> napsal:
> >> I afraid so with generic multiragetype there lot of array
> infrastructure will be duplicated
> >
> > on second hand - it is true so classic array concat is not optimal for
> set of ranges, so some functionality should be redefined every time.
> >
> > I don't know what is possible, but for me - multiranges is special kind
> (subset) of arrays and can be implement as subset of arrays. I remember
> other possible kind of arrays - "sets" without duplicates. It is similar
> case, I think.
> >
> > Maybe introduction of multirages as new generic type is bad direction,
> and can be better and more enhanceable in future to introduce some like
> special kinds of arrays. So for example - unnest can be used directly for
> arrays and multiranges too - because there will be common base.
>
> Well I'm afraid of that too a bit, although I also agree it may be an
> opportunity to share some common behavior and implementation. For
> example in the discussion about string syntax, I think keeping it the
> same as arrays is nicer for people and lets us share more between the
> two types.
>
> That said I don't think a multirange is a subtype of arrays (speaking
> as a traditional object-oriented subtype), just something that shares
> a lot of the same behavior. I'm inclined to maximize the overlap where
> feasible though, e.g. string syntax, UNNEST, indexing, function naming
> (`range_length`), etc. Something like Rust traits (or Java interfaces)
> seems a closer mental model, not that we have to formalize that
> somehow, particularly up front.
>
A introduction of new generic type can have some other impacts - there can
be necessary special support for PL languages.
I understand so it is hard to decide - because we miss some more generic
base "sets".
Probably we cannot to think more about it now, and we have to wait to some
patches. Later we can see how much code is duplicated and if it is a
problem or not.
Regards
Pavel
> Yours,
> Paul
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-07-09 15:51 ` David Fetter <david@fetter.org>
2019-07-09 16:40 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
4 siblings, 1 reply; 152+ messages in thread
From: David Fetter @ 2019-07-09 15:51 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Mon, Jul 08, 2019 at 09:46:44AM -0700, Paul A Jungwirth wrote:
> On Sat, Jul 6, 2019 at 12:13 PM Jeff Davis <pgsql@j-davis.com> wrote:
> >
> > On Fri, 2019-07-05 at 09:58 -0700, Paul A Jungwirth wrote:
> > > user-defined range types. So how about I start on it and see how it
> > > goes? I expect I can follow the existing code for range types pretty
> > > closely, so maybe it won't be too hard.
> >
> > That would be great to at least take a look. If it starts to look like
> > a bad idea, then we can re-evaluate and see if it's better to just
> > return arrays.
>
> I made some progress over the weekend. I don't have a patch yet but I
> thought I'd ask for opinions on the approach I'm taking:
>
> - A multirange type is an extra thing you get when you define a range
> (just like how you get a tstzrange[]). Therefore....
> - I don't need separate commands to add/drop multirange types. You get
> one when you define a range type, and if you drop a range type it gets
> dropped automatically.
Yay for fewer manual steps!
> - I'm adding a new typtype for multiranges. ('m' in pg_type).
> - I'm just adding a mltrngtypeid column to pg_range. I don't think I
> need a new pg_multirange table.
Makes sense, as they'd no longer be separate concepts.
> - You can have a multirange[].
I can see how that would fall out of this, but I'm a little puzzled as
to what people might use it for. Aggregates, maybe?
> - Multirange in/out work just like arrays, e.g. '{"[1,3)", "[5,6)"}'
> - I'll add an anymultirange pseudotype. When it's the return type of a
> function that has an "anyrange" parameter, it will use the same base
> element type. (So basically anymultirange : anyrange :: anyarray ::
> anyelement.)
Neat!
> - You can cast from a multirange to an array. The individual ranges
> are always sorted in the result array.
Is this so people can pick individual ranges out of the multirange,
or...? Speaking of casts, it's possible that a multirange is also a
range. Would it make sense to have a cast from multirange to range?
> - You can cast from an array to a multirange but it will error if
> there are overlaps (or not?).
An alternative would be to canonicalize into non-overlapping ranges.
There's some precedent for this in casts to JSONB. Maybe a function
that isn't a cast should handle such things.
> The array's ranges don't have to be sorted but they will be after a
> "round trip".
Makes sense.
> - Interesting functions:
> - multirange_length
Is that the sum of the lengths of the ranges? Are we guaranteeing a
measure in addition to ordering on ranges now?
> - range_agg (range_union_agg if you like)
> - range_intersection_agg
> - You can subscript a multirange like you do an array (? This could be
> a function instead.)
How would this play with the generic subscripting patch in flight?
> - operators:
> - union (++) and intersection (*):
> - We already have + for range union but it raises an error if
> there is a gap, so ++ is the same but with no errors.
> - r ++ r = mr (commutative, associative)
> - mr ++ r = mr
> - r ++ mr = mr
> - r * r = r (unchanged)
> - mr * r = r
> - r * mr = r
Shouldn't the two above both yield multirange ? For example, if I
understand correctly,
{"[1,3)","[5,7)"} * [2,6) should be {"[2,3)","[5,6)"}
> - mr - r = mr
> - r - mr = mr
> - mr - mr = mr
> - comparison
> - mr = mr
> - mr @> x
x is in the domain of the (multi)range?
> - mr @> r
> - mr @> mr
> - x <@ mr
> - r <@ mr
> - mr <@ mr
> - mr << mr (strictly left of)
> - mr >> mr (strictly right of)
> - mr &< mr (does not extend to the right of)
> - mr &> mr (does not extend to the left of)
> - inverse operator?:
> - the inverse of {"[1,2)"} would be {"[null, 1)", "[2, null)"}.
Is that the same as ["(∞, ∞)"] - {"[1,2)"}? I seem to recall that the
usual convention (at least in math) is to use intervals that are
generally represented as open on the infinity side, but that might not
fit how we do things.
> - not sure we want this or what the symbol should be. I don't like
> -mr as an inverse because then mr - mr != mr ++ -mr.
!mr , perhaps?
Best,
David.
--
David Fetter <david(at)fetter(dot)org> http://fetter.org/
Phone: +1 415 235 3778
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 15:51 ` Re: range_agg David Fetter <david@fetter.org>
@ 2019-07-09 16:40 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-10 06:24 ` Re: range_agg David Fetter <david@fetter.org>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2019-07-09 16:40 UTC (permalink / raw)
To: David Fetter <david@fetter.org>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Tue, Jul 9, 2019 at 8:51 AM David Fetter <david@fetter.org> wrote:
> > - A multirange type is an extra thing you get when you define a range
> > (just like how you get a tstzrange[]). Therefore....
> > - I don't need separate commands to add/drop multirange types. You get
> > one when you define a range type, and if you drop a range type it gets
> > dropped automatically.
>
> Yay for fewer manual steps!
Thanks for taking a look and sharing your thoughts!
> > - You can have a multirange[].
>
> I can see how that would fall out of this, but I'm a little puzzled as
> to what people might use it for. Aggregates, maybe?
I don't know either, but I thought it was standard to define a T[] for
every T. Anyway it doesn't seem difficult.
> > - You can cast from a multirange to an array. The individual ranges
> > are always sorted in the result array.
>
> Is this so people can pick individual ranges out of the multirange,
> or...?
Yes. I want this for foreign keys actually, where I construct a
multirange and ask for just its first range.
> Speaking of casts, it's possible that a multirange is also a
> range. Would it make sense to have a cast from multirange to range?
Hmm, that seems strange to me. You don't cast from an array to one of
its elements. If we have subscripting, why use casting to get the
first element?
> > - You can cast from an array to a multirange but it will error if
> > there are overlaps (or not?).
>
> An alternative would be to canonicalize into non-overlapping ranges.
> There's some precedent for this in casts to JSONB. Maybe a function
> that isn't a cast should handle such things.
I agree it'd be nice to have both.
> > - Interesting functions:
> > - multirange_length
>
> Is that the sum of the lengths of the ranges? Are we guaranteeing a
> measure in addition to ordering on ranges now?
Just the number of disjoint ranges in the multirange.
> > - You can subscript a multirange like you do an array (? This could be
> > a function instead.)
>
> How would this play with the generic subscripting patch in flight?
I'm not aware of that patch but I guess I better check it out. :-)
> > - operators:
> > - mr * r = r
> > - r * mr = r
>
> Shouldn't the two above both yield multirange ? For example, if I
> understand correctly,
You're right! Thanks for the correction.
> > - comparison
> > - mr = mr
> > - mr @> x
>
> x is in the domain of the (multi)range?
Yes. It's the scalar base type the range type is based on. I had in
mind the math/ML convention of `x` for scalar and `X` for
vector/matrix.
> > - inverse operator?:
> > - the inverse of {"[1,2)"} would be {"[null, 1)", "[2, null)"}.
>
> Is that the same as ["(∞, ∞)"] - {"[1,2)"}?
Yes.
> I seem to recall that the
> usual convention (at least in math) is to use intervals that are
> generally represented as open on the infinity side, but that might not
> fit how we do things.
I think it does, unless I'm misunderstanding?
> > - not sure we want this or what the symbol should be. I don't like
> > -mr as an inverse because then mr - mr != mr ++ -mr.
>
> !mr , perhaps?
I like that suggestion. Honestly I'm not sure we even want an inverse,
but it's so important theoretically we should at least consider
whether it is appropriate here. Or maybe "inverse" is the wrong word
for this, or there is a different meaning it should have.
Thanks,
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 15:51 ` Re: range_agg David Fetter <david@fetter.org>
2019-07-09 16:40 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-07-10 06:24 ` David Fetter <david@fetter.org>
2019-07-10 14:55 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: David Fetter @ 2019-07-10 06:24 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Tue, Jul 09, 2019 at 09:40:59AM -0700, Paul A Jungwirth wrote:
> On Tue, Jul 9, 2019 at 8:51 AM David Fetter <david@fetter.org> wrote:
> > > - A multirange type is an extra thing you get when you define a range
> > > (just like how you get a tstzrange[]). Therefore....
> > > - I don't need separate commands to add/drop multirange types. You get
> > > one when you define a range type, and if you drop a range type it gets
> > > dropped automatically.
> >
> > Yay for fewer manual steps!
>
> Thanks for taking a look and sharing your thoughts!
>
> > > - You can have a multirange[].
> >
> > I can see how that would fall out of this, but I'm a little puzzled as
> > to what people might use it for. Aggregates, maybe?
>
> I don't know either, but I thought it was standard to define a T[] for
> every T. Anyway it doesn't seem difficult.
>
> > > - You can cast from a multirange to an array. The individual ranges
> > > are always sorted in the result array.
> >
> > Is this so people can pick individual ranges out of the multirange,
> > or...?
>
> Yes. I want this for foreign keys actually, where I construct a
> multirange and ask for just its first range.
I'm sure I'll understand this better once I get my head around
temporal foreign keys.
> > Speaking of casts, it's possible that a multirange is also a
> > range. Would it make sense to have a cast from multirange to range?
>
> Hmm, that seems strange to me. You don't cast from an array to one of
> its elements. If we have subscripting, why use casting to get the
> first element?
Excellent point.
> > > - Interesting functions:
> > > - multirange_length
> >
> > Is that the sum of the lengths of the ranges? Are we guaranteeing a
> > measure in addition to ordering on ranges now?
>
> Just the number of disjoint ranges in the multirange.
Thanks for clarifying.
> > > - You can subscript a multirange like you do an array (? This could be
> > > a function instead.)
> >
> > How would this play with the generic subscripting patch in flight?
>
> I'm not aware of that patch but I guess I better check it out. :-)
Looks like I'm the second to mention it. Worth a review?
> > > - inverse operator?:
> > > - the inverse of {"[1,2)"} would be {"[null, 1)", "[2, null)"}.
> >
> > Is that the same as ["(∞, ∞)"] - {"[1,2)"}?
>
> Yes.
>
> > I seem to recall that the usual convention (at least in math) is
> > to use intervals that are generally represented as open on the
> > infinity side, but that might not fit how we do things.
>
> I think it does, unless I'm misunderstanding?
Oh, I was just wondering about the square bracket on the left side of
[null, 1). It's not super important.
> > > - not sure we want this or what the symbol should be. I don't like
> > > -mr as an inverse because then mr - mr != mr ++ -mr.
> >
> > !mr , perhaps?
>
> I like that suggestion. Honestly I'm not sure we even want an inverse,
> but it's so important theoretically we should at least consider
> whether it is appropriate here. Or maybe "inverse" is the wrong word
> for this, or there is a different meaning it should have.
Jeff's suggestion of ~ for complement is better.
Best,
David.
--
David Fetter <david(at)fetter(dot)org> http://fetter.org/
Phone: +1 415 235 3778
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 15:51 ` Re: range_agg David Fetter <david@fetter.org>
2019-07-09 16:40 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-10 06:24 ` Re: range_agg David Fetter <david@fetter.org>
@ 2019-07-10 14:55 ` Paul Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 0 replies; 152+ messages in thread
From: Paul Jungwirth @ 2019-07-10 14:55 UTC (permalink / raw)
To: David Fetter <david@fetter.org>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On 7/9/19 11:24 PM, David Fetter wrote:
>>> I seem to recall that the usual convention (at least in math) is
>>> to use intervals that are generally represented as open on the
>>> infinity side, but that might not fit how we do things.
>>
>> I think it does, unless I'm misunderstanding?
>
> Oh, I was just wondering about the square bracket on the left side of
> [null, 1). It's not super important.
Ah, I understand now. Just a typo on my part. Thanks for catching it,
and sorry for the confusion!
>>> !mr , perhaps?
>>
>> I like that suggestion. Honestly I'm not sure we even want an inverse,
>> but it's so important theoretically we should at least consider
>> whether it is appropriate here. Or maybe "inverse" is the wrong word
>> for this, or there is a different meaning it should have.
>
> Jeff's suggestion of ~ for complement is better.
Okay, thanks. I like it better too.
Yours,
--
Paul ~{:-)
pj@illuminatedcomputing.com
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-07-09 19:01 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2019-07-09 19:05 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
4 siblings, 1 reply; 152+ messages in thread
From: Alvaro Herrera @ 2019-07-09 19:01 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On 2019-Jul-08, Paul A Jungwirth wrote:
> - You can subscript a multirange like you do an array (? This could be
> a function instead.)
Note that we already have a patch in the pipe to make subscripting an
extensible operation, which would fit pretty well here, I think.
Also, I suppose you would need unnest(multirange) to yield the set of
ranges.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 19:01 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2019-07-09 19:05 ` Paul Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 0 replies; 152+ messages in thread
From: Paul Jungwirth @ 2019-07-09 19:05 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On 7/9/19 12:01 PM, Alvaro Herrera wrote:
> On 2019-Jul-08, Paul A Jungwirth wrote:
>
>> - You can subscript a multirange like you do an array (? This could be
>> a function instead.)
>
> Note that we already have a patch in the pipe to make subscripting an
> extensible operation, which would fit pretty well here, I think.
I'll take a look at that!
> Also, I suppose you would need unnest(multirange) to yield the set of
> ranges.
I think that would be really nice, although it isn't critical I think if
you can do something like UNNEST(multirange::tstzrange[]).
--
Paul ~{:-)
pj@illuminatedcomputing.com
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-07-09 19:02 ` Jeff Davis <pgsql@j-davis.com>
2019-07-10 04:18 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
4 siblings, 1 reply; 152+ messages in thread
From: Jeff Davis @ 2019-07-09 19:02 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: pgsql-hackers
On Mon, 2019-07-08 at 09:46 -0700, Paul A Jungwirth wrote:
> - A multirange type is an extra thing you get when you define a range
> (just like how you get a tstzrange[]). Therefore....
Agreed.
> - I'm adding a new typtype for multiranges. ('m' in pg_type).
Sounds reasonable.
> - I'm just adding a mltrngtypeid column to pg_range. I don't think I
> need a new pg_multirange table.
> - You can have a multirange[].
> - Multirange in/out work just like arrays, e.g. '{"[1,3)", "[5,6)"}'
It would be cool to have a better text representation. We could go
simple like:
'[1,3) [5,6)'
Or maybe someone has another idea how to represent a multirange to be
more visually descriptive?
> - I'll add an anymultirange pseudotype. When it's the return type of
> a
> function that has an "anyrange" parameter, it will use the same base
> element type. (So basically anymultirange : anyrange :: anyarray ::
> anyelement.)
I like it.
> - range_agg (range_union_agg if you like)
> - range_intersection_agg
I'm fine with those names.
> - You can subscript a multirange like you do an array (? This could
> be
> a function instead.)
I wouldn't try to hard to make them subscriptable. I'm not opposed to
it, but it's easy enough to cast to an array and then subscript.
> - operators:
> - union (++) and intersection (*):
> - We already have + for range union but it raises an error if
> there is a gap, so ++ is the same but with no errors.
> - r ++ r = mr (commutative, associative)
> - mr ++ r = mr
> - r ++ mr = mr
I like it.
> - inverse operator?:
> - the inverse of {"[1,2)"} would be {"[null, 1)", "[2, null)"}.
> - not sure we want this or what the symbol should be. I don't
> like
> -mr as an inverse because then mr - mr != mr ++ -mr.
I think "complement" might be a better name than "inverse".
m1 - m2 = m1 * complement(m2)
What about "~"?
There will be some changes to parse_coerce.c, just like in range types.
I took a brief look here and it looks pretty reasonable; hopefully
there aren't any hidden surprises.
Regards,
Jeff Davis
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 19:02 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
@ 2019-07-10 04:18 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 0 replies; 152+ messages in thread
From: Paul A Jungwirth @ 2019-07-10 04:18 UTC (permalink / raw)
To: Jeff Davis <pgsql@j-davis.com>; +Cc: pgsql-hackers
On Tue, Jul 9, 2019 at 12:02 PM Jeff Davis <pgsql@j-davis.com> wrote:
> > - Multirange in/out work just like arrays, e.g. '{"[1,3)", "[5,6)"}'
>
> It would be cool to have a better text representation. We could go
> simple like:
>
> '[1,3) [5,6)'
Will that work with all ranges, even user-defined ones? With a
tstzrange[] there is a lot of quoting:
=# select array[tstzrange(now(), now() + interval '1 hour')];
array
---------------------------------------------------------------------------
{"[\"2019-07-09 12:40:20.794054-07\",\"2019-07-09 13:40:20.794054-07\")"}
I'm more inclined to follow the array syntax both because it will be
familiar & consistent to users (no need to remember any differences)
and because it's already built so we can use it and know it won't have
gotchas.
> I think "complement" might be a better name than "inverse".
>
> m1 - m2 = m1 * complement(m2)
>
> What about "~"?
That seems like the right term and a good symbol.
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-08-17 17:47 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
4 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2019-08-17 17:47 UTC (permalink / raw)
To: Jeff Davis <pgsql@j-davis.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: pgsql-hackers
On Mon, Jul 8, 2019 at 9:46 AM Paul A Jungwirth
<pj@illuminatedcomputing.com> wrote:
> - A multirange type is an extra thing you get when you define a range
> (just like how you get a tstzrange[]). Therefore....
I've been able to make a little more progress on multiranges the last
few days, but it reminded me of an open question I've had for awhile:
typmods! I see places in the range code that gesture toward supporting
typmods, but none of the existing range types permit them. For
example:
postgres=# select '5'::numeric(4,2);
numeric
---------
5.00
(1 row)
postgres=# select '[1,4)'::numrange(4,2);
ERROR: type modifier is not allowed for type "numrange"
LINE 1: select '[1,4)'::numrange(4,2);
So I'm wondering how seriously I should take this for multiranges? I
guess if a range type did support typmods, it would just delegate to
the underlying element type for their meaning, and so a multirange
should delegate it too? Is there any historical discussion around
typemods on range types?
Thanks!
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-08-21 05:33 ` Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Jeff Davis @ 2019-08-21 05:33 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: pgsql-hackers
On Sat, 2019-08-17 at 10:47 -0700, Paul A Jungwirth wrote:
> So I'm wondering how seriously I should take this for multiranges? I
> guess if a range type did support typmods, it would just delegate to
> the underlying element type for their meaning, and so a multirange
> should delegate it too? Is there any historical discussion around
> typemods on range types?
I did find a few references:
https://www.postgresql.org/message-id/1288029716.8645.4.camel%40jdavis-ux.asterdata.local
https://www.postgresql.org/message-id/20110111191334.GB11603%40fetter.org
https://www.postgresql.org/message-id/1296974485.27157.136.camel@jdavis
I'd be interested in ways that we can use a typmod-like concept to
improve the type system. Unfortunately, typmod is just not
sophisticated enough to do very much because it's lost through function
calls. Improving that would be a separate and challenging project.
So, I wouldn't spend a lot of time on typmod for multiranges.
Regards,
Jeff Davis
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
@ 2019-08-22 04:54 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2019-08-22 04:54 UTC (permalink / raw)
To: Jeff Davis <pgsql@j-davis.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Tue, Aug 20, 2019 at 10:33 PM Jeff Davis <pgsql@j-davis.com> wrote:
> > Is there any historical discussion around
> > typemods on range types?
>
> I did find a few references:
Thanks for looking those up! It's very interesting to see some of the
original discussion around range types.
Btw this is true of so much, isn't it?:
> It's more a property of the
> column than the type.
Sometimes I think about having a maybe<T> type instead of null/not
null. SQL nulls are already very "monadic" I think but nullability
doesn't travel. I feel like someone ought to write a paper about that,
but I don't know of any. This is tantalizingly close (and a fun read)
but really about something else:
https://www.researchgate.net/publication/266657590_Incomplete_data_what_went_wrong_and_how_to_fix_it
Since you're getting into Rust maybe you can update the wiki page
mentioned in those threads about refactoring the type system. :-)
Anyway sorry for the digression. . . .
> So, I wouldn't spend a lot of time on typmod for multiranges.
Okay, thanks! There is plenty else to do. I think I'm already
supporting it as much as range types do.
Btw I have working multirange_{send,recv,in,out} now, and I
automatically create a multirange type and its array type when someone
creates a new range type. I have a decent start on passing tests and
no compiler warnings. I also have a start on anymultirange and
anyrangearray. (I think I need the latter to support a range-like
constructor function, so you can say `int4multirange(int4range(1,4),
int4range(8,10))`.) I want to get the any* types done and improve the
test coverage, and then I'll probably be ready to share a patch.
Here are a couple other questions:
- Does anyone have advice for the typanalyze function? I feel pretty
out of my depth there (although I haven't looked into typanalyze stuff
very deeply yet). I can probably get some inspiration from
range_typanalyze and array_typanalyze, but those are both quite
detailed (their statistics functions that is).
- What should a multirange do if you give it an empty range? I'm
thinking it should just ignore it, but then `'{}'::int4multirange =
'{empty}'::int4multirange`. Does that seem okay? (It does to me
actually, if we think of `empty` as the additive identity. Similarly
mr + empty = mr.
- What should a multirange do if you give it a null, like
`int4multirange(int4range(1,4), null)`. I'm thinking it should be
null, just like mr + null = null. Right?
Thanks!
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-08-26 18:34 ` Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Jeff Davis @ 2019-08-26 18:34 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Wed, 2019-08-21 at 21:54 -0700, Paul A Jungwirth wrote:
> Sometimes I think about having a maybe<T> type instead of null/not
> null. SQL nulls are already very "monadic" I think but nullability
> doesn't travel.
Yeah, that would be a great direction, but there is some additional
complexity that we'd need to deal with that a "normal" compiler does
not:
* handling both existing global types (like int) as well as on-the-
fly types like Maybe<Either<Int,Bool>>
* types need to do more things, like have serialized representations,
interface with indexing strategies, and present the optimizer with
choices that may influence which indexes can be used or not
* at some point needs to work with normal SQL types and NULL
* there are a lot of times we care not just whether a type is
sortable, but we actually care about the way it's sorted (e.g.
localization). typeclasses+newtype would probably be unacceptable for
trying to match SQL behavior here.
I'm all in favor of pursuing this, but it's not going to bear fruit
very soon.
> Btw I have working multirange_{send,recv,in,out} now, and I
> automatically create a multirange type and its array type when
> someone
> creates a new range type. I have a decent start on passing tests and
> no compiler warnings. I also have a start on anymultirange and
> anyrangearray. (I think I need the latter to support a range-like
> constructor function, so you can say `int4multirange(int4range(1,4),
> int4range(8,10))`.) I want to get the any* types done and improve the
> test coverage, and then I'll probably be ready to share a patch.
Awesome!
> Here are a couple other questions:
>
> - Does anyone have advice for the typanalyze function? I feel pretty
> out of my depth there (although I haven't looked into typanalyze
> stuff
> very deeply yet). I can probably get some inspiration from
> range_typanalyze and array_typanalyze, but those are both quite
> detailed (their statistics functions that is).
I think Alexander Korotkov did a lot of the heavy lifting here, perhaps
he has a comment? I'd keep it simple for now if you can, and we can try
to improve it later.
> - What should a multirange do if you give it an empty range? I'm
> thinking it should just ignore it, but then `'{}'::int4multirange =
> '{empty}'::int4multirange`. Does that seem okay? (It does to me
> actually, if we think of `empty` as the additive identity. Similarly
> mr + empty = mr.
I agree. Multiranges are more than just an array of ranges, so they
coalesce into some canonical form.
> - What should a multirange do if you give it a null, like
> `int4multirange(int4range(1,4), null)`. I'm thinking it should be
> null, just like mr + null = null. Right?
Yes. NULL is for the overall multirange datum (that is, a multirange
column can be NULL), but I don't think individual parts of a datatype
make much sense as NULL. So, I agree that mr + null = null. (Note that
arrays and records can have NULL parts, but I don't see a reason we
should follow those examples for multiranges.)
Regards,
Jeff Davis
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
@ 2019-09-01 13:26 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2019-09-01 13:26 UTC (permalink / raw)
To: Jeff Davis <pgsql@j-davis.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
> > Btw I have working multirange_{send,recv,in,out} now. . . .
Just about all the other operators are done too, but I wonder what
symbols people like for union and minus? Range uses + for union. I
have working code and tests that adds this:
r + mr = mr
mr + r = mr
mr + mr = mr
But I would like to use a different symbol instead, like ++, so I can
have all four:
r ++ r = mr
r ++ mr = mr
mr ++ r = mr
mr ++ mr = mr
(The existing r + r operator throws an error if the inputs have a gap.)
The trouble is that ++ isn't allowed. (Neither is --.) From
https://www.postgresql.org/docs/11/sql-createoperator.html :
> A multicharacter operator name cannot end in + or -, unless the name also contains at least one of these characters:
> ~ ! @ # % ^ & | ` ?
So are there any other suggestions? I'm open to arguments that I
should just use +, but I think having a way to add two simple ranges
and get a multirange would be nice too, so my preference is to find a
new operator. It should work with minus and intersection (* for
ranges) too. Some proposals:
+* and -* and ** (* as in regex "zero or many" reminds me of how a
multirange holds zero or many ranges. ** is confusing though because
it's like exponentiation.)
@+ and @- and @* (I dunno why but I kind of like it. We already have @> and <@.)
<+> and <-> and <*> (I was hoping for (+) etc for the math connection
to circled operators, but this is close. Maybe this would be stronger
if multirange_{in,out} used <> delims instead of {}, although I also
like how {} is consistent with arrays.)
Anyone else have any thoughts?
Thanks,
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-09-05 17:15 ` Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Jeff Davis @ 2019-09-05 17:15 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Sun, 2019-09-01 at 06:26 -0700, Paul A Jungwirth wrote:
> @+ and @- and @* (I dunno why but I kind of like it. We already have
> @> and <@.)
I think I like this proposal best; it reminds me of perl. Though some
might say that's an argument against it.
Regards,
Jeff Davis
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
@ 2019-09-05 17:45 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2019-09-05 17:45 UTC (permalink / raw)
To: Jeff Davis <pgsql@j-davis.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Sep 5, 2019 at 10:15 AM Jeff Davis <pgsql@j-davis.com> wrote:
>
> On Sun, 2019-09-01 at 06:26 -0700, Paul A Jungwirth wrote:
> > @+ and @- and @* (I dunno why but I kind of like it. We already have
> > @> and <@.)
>
> I think I like this proposal best; it reminds me of perl. Though some
> might say that's an argument against it.
Thanks Jeff, it's my favorite too. :-) Strangely it feels the hardest
to justify. Right now I have + and - and * implemented but I'll change
them to @+ and @- and @* so that I can support `range R range =
multirange` too.
Btw is there any reason to send a "preview" patch with my current
progress, since we're starting a new commit fest? Here is what I have
left to do:
- Change those three operators.
- Write range_intersect_agg. (range_agg is done but needs some tests
before I commit it.)
- Write documentation.
- Add multiranges to resolve_generic_type, and figure out how to test
that (see the other thread about two latent range-related bugs there).
- Rebase on current master. (It should be just a few weeks behind right now.)
- Run pgindent to make sure I'm conforming to whitespace/style guidelines.
- Split it up into a few separate patch files.
Right now I'm planning to do all that before sending a patch. I'm
happy to send something something in-progress too, but I don't want to
waste any reviewers' time. If folks want an early peak though let me
know. (You can also find my messy progress at
https://github.com/pjungwir/postgresql/tree/multirange)
Also here are some other items that won't be in my next patch, but
should probably be done (maybe by someone else but I'm happy to figure
it out too) before this is really committed:
- typanalyze
- selectivity
- gist support
- spgist support
If anyone would like to help with those, let me know. :-)
Yours,
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-09-05 18:52 ` Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Jeff Davis @ 2019-09-05 18:52 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, 2019-09-05 at 10:45 -0700, Paul A Jungwirth wrote:
> Right now I'm planning to do all that before sending a patch. I'm
> happy to send something something in-progress too, but I don't want
> to
> waste any reviewers' time. If folks want an early peak though let me
> know. (You can also find my messy progress at
> https://github.com/pjungwir/postgresql/tree/multirange)
Sounds good. The rule I use is: "will the feedback I get be helpful, or
just tell me about obvious problems I already know about".
Regards,
Jeff Davis
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
@ 2019-09-22 04:50 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2019-09-22 04:50 UTC (permalink / raw)
To: Jeff Davis <pgsql@j-davis.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers
On Thu, Sep 5, 2019 at 11:52 AM Jeff Davis <pgsql@j-davis.com> wrote:
>
> On Thu, 2019-09-05 at 10:45 -0700, Paul A Jungwirth wrote:
> > Right now I'm planning to do all that before sending a patch. I'm
> > happy to send something something in-progress too, but I don't want
> > to
> > waste any reviewers' time. If folks want an early peak though let me
> > know. (You can also find my messy progress at
> > https://github.com/pjungwir/postgresql/tree/multirange)
>
> Sounds good. The rule I use is: "will the feedback I get be helpful, or
> just tell me about obvious problems I already know about".
Here are some patches to add multiranges. I tried to split things up a
bit but most things landed in parts 1 & 2.
Things I haven't done (but would be interested in doing or getting help with):
- gist opclass
- spgist opclass
- typanalyze
- selectivity
- anyrangearray
- anymultirangearray?
- UNNEST for multirange and/or a way to convert it to an array
- indexing/subscripting (see patch for standardized subscripting)
Attachments:
[application/octet-stream] v3-0003-multirange-pg_dump.patch (1.6K, ../../CA+renyWOSQ5P-=KCqti4WN1hdS9h9O5kFSvXqodq49XaR43HkA@mail.gmail.com/2-v3-0003-multirange-pg_dump.patch)
download | inline diff:
From c372b651a8d575cf3c6ed80e4f581ef03373c06a Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:37 -0700
Subject: [PATCH v3 3/4] multirange pg_dump
---
src/bin/pg_dump/pg_dump.c | 7 ++++++-
src/bin/pg_dump/pg_dump.h | 1 +
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index f01fea5b91..124ee1a842 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1586,7 +1586,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4926,6 +4926,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index ec5a924b8f..98ba052d5b 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -175,6 +175,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
--
2.11.0
[application/octet-stream] v3-0004-multirange-docs.patch (25.4K, ../../CA+renyWOSQ5P-=KCqti4WN1hdS9h9O5kFSvXqodq49XaR43HkA@mail.gmail.com/3-v3-0004-multirange-docs.patch)
download | inline diff:
From 8d1c8596d5712acc3719d5e734e6c49847477f06 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:47 -0700
Subject: [PATCH v3 4/4] multirange docs
---
doc/src/sgml/extend.sgml | 28 +++-
doc/src/sgml/func.sgml | 302 +++++++++++++++++++++++++++++++++++++++----
doc/src/sgml/rangetypes.sgml | 47 ++++++-
3 files changed, 342 insertions(+), 35 deletions(-)
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 8dc2b893f7..0bda0f35bf 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -229,9 +229,9 @@
</indexterm>
<para>
- Five pseudo-types of special interest are <type>anyelement</type>,
+ Six pseudo-types of special interest are <type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
- and <type>anyrange</type>,
+ <type>anyrange</type>, and <type>anymultirange</type>.
which are collectively called <firstterm>polymorphic types</firstterm>.
Any function declared using these types is said to be
a <firstterm>polymorphic function</firstterm>. A polymorphic function can
@@ -250,15 +250,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type>, the actual range type in
- the <type>anyrange</type> positions must be a range whose subtype is
- the same type appearing in the <type>anyelement</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -268,6 +268,20 @@
</para>
<para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type>, the actual range type in
+ the <type>anyrange</type> positions must be a range whose subtype is
+ the same type appearing in the <type>anyelement</type> positions.
+ The type <type>anymultirange</type> accepts a multirange
+ whose contents match the other polymorphic types.
+ That is it must hold ranges matching <type>anyrange</type>
+ and base type elements matching <type>anyelement</type> (if either of
+ those are present). If <type>anyarray</type> is present its elements
+ must match the base type of the <type>anyrange</type> and/or the base
+ type of the ranges in an </type>anymultirange</type>.
+ </para>
+
+ <para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
types are allowed. For example, a function declared as
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 567d2ecf3a..2eb1ccfc1f 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14473,7 +14473,9 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14483,7 +14485,7 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14491,136 +14493,247 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>f</literal></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)</literallayout></entry>
<entry><literal>[5,20)</literal></entry>
</row>
<row>
+ <entry> <literal>@+</literal> </entry>
+ <entry>safe union</entry>
+ <entry>
+ <literallayout class="monospaced">numrange(5,10) @+ numrange(15,20)
+numrange(5,10) @+ '{[15,20)}'::nummultirange
+'{[5,10)}'::nummultirange @+ numrange(15,20)
+'{[5,10)}'::nummultirange @+ '{[15,20)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literal>{[5,10), [15,20)}</literal></entry>
+ </row>
+
+ <row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)</literallayout></entry>
<entry><literal>[10,15)</literal></entry>
</row>
<row>
+ <entry> <literal>@*</literal> </entry>
+ <entry>safe intersection</entry>
+ <entry>
+ <literallayout class="monospaced">int8range(5,15) @* int8range(10,20)
+int8range(5,15) @* '{[10,20)}'::int8multirange
+'{[5,15)}'::int8multirange @* int8range(10,20)
+'{[5,15)}'::int8multirange @* '{[10,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literal>{[10,15)}</literal></entry>
+ </row>
+
+ <row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)</literallayout></entry>
<entry><literal>[5,10)</literal></entry>
</row>
+ <row>
+ <entry> <literal>@-</literal> </entry>
+ <entry>safe difference</entry>
+ <entry>
+ <literallayout class="monospaced">int8range(5,20) @- int8range(10,15)
+int8range(5,20) @- '{[10,15)}'::int8multirange
+'{[5,20)}'::int8multirange @- int8range(10,15)
+'{[5,20)}'::int8multirange @- '{[10,15)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literal>{[5,10), [15,20)}</literal></entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -14636,19 +14749,28 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. The safe versions of union, intersection, and difference
+ return multiranges, so they can handle gaps without failing.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14700,6 +14822,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14711,6 +14844,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>isempty</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14722,6 +14866,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14733,6 +14888,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14744,6 +14910,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14755,6 +14932,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14766,6 +14954,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>range_merge</function>(<type>anyrange</type>, <type>anyrange</type>)
</literal>
</entry>
@@ -14774,16 +14973,27 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -15105,6 +15315,44 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>string_agg</primary>
</indexterm>
<function>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index 3a034d9b06..3e37b352fc 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -235,10 +242,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -272,6 +299,19 @@ SELECT int8range(1, 14, '(]');
SELECT numrange(NULL, 2.2);
</programlisting>
</para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
+</programlisting>
+ </para>
</sect2>
<sect2 id="rangetypes-discrete">
@@ -345,6 +385,11 @@ SELECT '[1.234, 5.678]'::floatrange;
</para>
<para>
+ When you define your own range your automatically get a corresponding
+ multirange type.
+ </para>
+
+ <para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
ordering that determines which values fall into a given range.
--
2.11.0
[application/octet-stream] v3-0001-multirange-type-basics.patch (141.0K, ../../CA+renyWOSQ5P-=KCqti4WN1hdS9h9O5kFSvXqodq49XaR43HkA@mail.gmail.com/4-v3-0001-multirange-type-basics.patch)
download | inline diff:
From ede3130324d795005e195961547d5305336a96ad Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:25:39 -0700
Subject: [PATCH v3 1/4] multirange type basics
---
src/backend/catalog/pg_proc.c | 16 +-
src/backend/catalog/pg_range.c | 11 +-
src/backend/catalog/pg_type.c | 134 +++-
src/backend/commands/typecmds.c | 171 ++++-
src/backend/parser/parse_coerce.c | 301 +++++++-
src/backend/utils/adt/Makefile | 2 +-
src/backend/utils/adt/multirangetypes.c | 966 ++++++++++++++++++++++++++
src/backend/utils/adt/pseudotypes.c | 25 +
src/backend/utils/adt/rangetypes.c | 155 ++++-
src/backend/utils/cache/lsyscache.c | 62 ++
src/backend/utils/cache/syscache.c | 11 +
src/backend/utils/cache/typcache.c | 109 ++-
src/backend/utils/fmgr/funcapi.c | 271 +++++++-
src/include/access/tupmacs.h | 4 +-
src/include/catalog/catversion.h | 2 +-
src/include/catalog/indexing.h | 3 +
src/include/catalog/pg_amop.dat | 22 +
src/include/catalog/pg_amproc.dat | 7 +
src/include/catalog/pg_opclass.dat | 4 +
src/include/catalog/pg_operator.dat | 33 +
src/include/catalog/pg_opfamily.dat | 4 +
src/include/catalog/pg_proc.dat | 77 ++
src/include/catalog/pg_range.dat | 15 +-
src/include/catalog/pg_range.h | 5 +-
src/include/catalog/pg_type.dat | 39 ++
src/include/catalog/pg_type.h | 8 +-
src/include/utils/lsyscache.h | 3 +
src/include/utils/multirangetypes.h | 72 ++
src/include/utils/rangetypes.h | 2 +
src/include/utils/syscache.h | 1 +
src/include/utils/typcache.h | 6 +
src/pl/plpgsql/src/pl_comp.c | 5 +
src/test/regress/expected/hash_func.out | 13 +
src/test/regress/expected/multirangetypes.out | 292 ++++++++
src/test/regress/expected/opr_sanity.out | 23 +-
src/test/regress/expected/type_sanity.out | 46 +-
src/test/regress/parallel_schedule | 3 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/hash_func.sql | 10 +
src/test/regress/sql/multirangetypes.sql | 65 ++
src/test/regress/sql/opr_sanity.sql | 18 +-
src/test/regress/sql/type_sanity.sql | 16 +-
42 files changed, 2895 insertions(+), 138 deletions(-)
create mode 100644 src/backend/utils/adt/multirangetypes.c
create mode 100644 src/include/utils/multirangetypes.h
create mode 100644 src/test/regress/expected/multirangetypes.out
create mode 100644 src/test/regress/sql/multirangetypes.sql
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index ef009ad2bc..80cc0721a5 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -192,6 +192,7 @@ ProcedureCreate(const char *procedureName,
genericInParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericInParam = true;
anyrangeInParam = true;
break;
@@ -219,6 +220,7 @@ ProcedureCreate(const char *procedureName,
genericOutParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericOutParam = true;
anyrangeOutParam = true;
break;
@@ -231,10 +233,10 @@ ProcedureCreate(const char *procedureName,
/*
* Do not allow polymorphic return type unless at least one input argument
- * is polymorphic. ANYRANGE return type is even stricter: must have an
- * ANYRANGE input (since we can't deduce the specific range type from
- * ANYELEMENT). Also, do not allow return type INTERNAL unless at least
- * one input argument is INTERNAL.
+ * is polymorphic. ANYRANGE and ANYMULTIRANGE return types are even
+ * stricter: must have an ANYRANGE or ANYMULTIRANGE input (since we can't
+ * deduce the specific range type from ANYELEMENT). Also, do not allow
+ * return type INTERNAL unless at least one input argument is INTERNAL.
*/
if ((IsPolymorphicType(returnType) || genericOutParam)
&& !genericInParam)
@@ -243,12 +245,12 @@ ProcedureCreate(const char *procedureName,
errmsg("cannot determine result data type"),
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
- if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
- !anyrangeInParam)
+ if ((returnType == ANYRANGEOID || returnType == ANYMULTIRANGEOID
+ || anyrangeOutParam) && !anyrangeInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
- errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
+ errdetail("A function returning \"anyrange\" or \"anymultirange\" must have at least one \"anyrange\" or \"anymultirange\" argument.")));
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
ereport(ERROR,
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index e6e138babd..d914f04858 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_mltrngtypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,13 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 2a51501d8d..08552850c8 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -36,6 +36,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static int makeUniqueTypeName(char *dest, const char *typeName, size_t namelen,
+ Oid typeNamespace, bool tryOriginalName);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -785,34 +788,10 @@ makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
char *arr = (char *) palloc(NAMEDATALEN);
int namelen = strlen(typeName);
- Relation pg_type_desc;
- int i;
-
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- pg_type_desc = table_open(TypeRelationId, AccessShareLock);
-
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
-
- table_close(pg_type_desc, AccessShareLock);
+ int underscores;
- if (i >= NAMEDATALEN - 1)
+ underscores = makeUniqueTypeName(arr, typeName, namelen, typeNamespace, false);
+ if (underscores >= NAMEDATALEN - 1)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -883,3 +862,104 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * the caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char mltrng[NAMEDATALEN];
+ char *mltrngunique = (char *) palloc(NAMEDATALEN);
+ int namelen = strlen(rangeTypeName);
+ int rangelen;
+ char *rangestr;
+ int rangeoffset;
+ int underscores;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ strlcpy(mltrng, rangeTypeName, NAMEDATALEN);
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ rangeoffset = rangestr - rangeTypeName;
+ rangelen = strlen(rangestr);
+ strlcpy(mltrng + rangeoffset, "multi", NAMEDATALEN - rangeoffset);
+ strlcpy(mltrng + rangeoffset + 5, rangestr, NAMEDATALEN - rangeoffset - 5);
+ namelen += 5;
+ }
+ else
+ {
+ strlcpy(mltrng + namelen, "multirange", NAMEDATALEN - namelen);
+ namelen += 10;
+ }
+
+ underscores = makeUniqueTypeName(mltrngunique, mltrng, namelen, typeNamespace, true);
+ if (underscores >= NAMEDATALEN - 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mltrngunique;
+}
+
+/*
+ * makeUniqueTypeName: Prepend underscores as needed until we make a name that
+ * doesn't collide with anything. Tries the original typeName if requested.
+ *
+ * Returns the number of underscores added.
+ */
+int
+makeUniqueTypeName(char *dest, const char *typeName, size_t namelen, Oid typeNamespace,
+ bool tryOriginalName)
+{
+ Relation pg_type_desc;
+ int i;
+
+ pg_type_desc = table_open(TypeRelationId, AccessShareLock);
+
+ for (i = 0; i < NAMEDATALEN - 1; i++)
+ {
+ if (i == 0)
+ {
+ if (tryOriginalName &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ {
+ strcpy(dest, typeName);
+ break;
+ }
+
+ }
+ else
+ {
+ dest[i - 1] = '_';
+ if (i + namelen < NAMEDATALEN)
+ strcpy(dest + i, typeName);
+ else
+ {
+ strlcpy(dest + i, typeName, NAMEDATALEN);
+ truncate_identifier(dest, NAMEDATALEN, false);
+ }
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ break;
+ }
+ }
+
+ table_close(pg_type_desc, AccessShareLock);
+
+ return i;
+}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 89887b8fd7..14a6857062 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -88,6 +88,8 @@ Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeArrayOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -811,7 +813,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1353,8 +1356,12 @@ DefineRange(CreateRangeStmt *stmt)
char *typeName;
Oid typeNamespace;
Oid typoid;
+ Oid mltrngtypoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1371,6 +1378,7 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1522,6 +1530,9 @@ DefineRange(CreateRangeStmt *stmt)
/* Allocate OID for array type */
rangeArrayOid = AssignTypeArrayOid();
+ /* Allocate OID for multirange array type */
+ multirangeArrayOid = AssignTypeArrayOid();
+
/* Create the pg_type entry */
address =
TypeCreate(InvalidOid, /* no predetermined type OID */
@@ -1557,9 +1568,47 @@ DefineRange(CreateRangeStmt *stmt)
InvalidOid); /* type's collation (ranges never have one) */
Assert(typoid == address.objectId);
+ /* Create the multirange that goes with it */
+
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(InvalidOid, /* no predetermined type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_MULTIRANGE, /* type-category (multirange type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ mltrngtypoid = mltrngaddress.objectId;
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, mltrngtypoid);
/*
* Create the array type that goes with it.
@@ -1600,8 +1649,49 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ mltrngtypoid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ mltrngtypoid, rangeArrayOid);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1679,6 +1769,83 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeArrayOid)
+{
+ static const char *const prosrc[2] = {"multirange_constructor0",
+ "multirange_constructor1"};
+ static const int pronargs[2] = {0, 1};
+
+ Oid constructorArgTypes[0];
+ ObjectAddress myself,
+ referenced;
+ int i;
+
+ constructorArgTypes[0] = rangeArrayOid;
+
+ Datum allParamTypes[1] = {ObjectIdGetDatum(rangeArrayOid)};
+ ArrayType *allParameterTypes = construct_array(allParamTypes, 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ Datum constructorAllParamTypes[2] = {PointerGetDatum(NULL), PointerGetDatum(allParameterTypes)};
+
+ Datum paramModes[1] = {CharGetDatum(FUNC_PARAM_VARIADIC)};
+ ArrayType *parameterModes = construct_array(paramModes, 1, CHAROID,
+ 1, true, 'c');
+ Datum constructorParamModes[2] = {PointerGetDatum(NULL), PointerGetDatum(parameterModes)};
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ for (i = 0; i < lengthof(prosrc); i++)
+ {
+ oidvector *constructorArgTypesVector;
+
+ constructorArgTypesVector = buildoidvector(constructorArgTypes,
+ pronargs[i]);
+
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace, /* namespace */
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR, /* language validator */
+ prosrc[i], /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ constructorArgTypesVector, /* parameterTypes */
+ constructorAllParamTypes[i], /* allParameterTypes */
+ constructorParamModes[i], /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+
+ /*
+ * Make the constructors internally-dependent on the multirange type
+ * so that they go away silently when the type is dropped. Note that
+ * pg_dump depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ }
+}
/*
* Find suitable I/O functions for a type.
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index a6c51a95da..f0b1f6f831 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -187,18 +187,20 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
- targetTypeId == ANYRANGEOID)
+ targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call anyarray_in,
- * anyenum_in, or anyrange_in, which will produce an error. Also, if
- * what we have is a domain over array, enum, or range, we have to
- * relabel it to its base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * anyarray_in, anyenum_in, anyrange_in, or anymultirange_in, which
+ * will produce an error. Also, if what we have is a domain over
+ * array, enum, range, or multirange, we have to relabel it to its
+ * base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1482,6 +1484,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid array_typelem;
Oid range_typeid = InvalidOid;
Oid range_typelem;
+ Oid multirange_typeid = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
@@ -1528,6 +1532,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1580,6 +1593,37 @@ check_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+ }
+
if (have_anynonarray)
{
/* require the element type to not be an array or domain over array */
@@ -1681,13 +1725,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid array_typelem;
Oid range_typelem;
+ Oid multirange_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
+ bool have_anyrange = (rettype == ANYRANGEOID);
+ bool have_anymultirange = (rettype == ANYMULTIRANGEOID);
/*
* Loop through the arguments to see if we have any that are polymorphic.
@@ -1745,7 +1793,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else if (decl_type == ANYRANGEOID)
{
- have_generics = true;
+ have_generics = have_anyrange = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
@@ -1763,6 +1811,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ have_generics = have_anymultirange = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
}
/*
@@ -1813,9 +1881,12 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- if (range_typeid == ANYRANGEOID && !have_anyelement)
+ if (range_typeid == ANYRANGEOID && !have_anyelement && !have_anymultirange)
{
- /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
+ /*
+ * Special case for ANYRANGE input: okay iff no ANYELEMENT or
+ * ANYMULTIRANGE
+ */
range_typelem = ANYELEMENTOID;
}
else
@@ -1849,6 +1920,69 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Get the range type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ if (multirange_typeid == ANYMULTIRANGEOID && !have_anyrange && !have_anyelement)
+ {
+ /*
+ * Special case for ANYMULTIRANGE input: okay iff no ANYRANGE or
+ * ANYELEMENT
+ */
+ multirange_typelem = ANYRANGEOID;
+ }
+ else
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+ }
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ /*
+ * Infer the element type too if needed (if there is an ANYMULTIRANGE
+ * and ANYELEMENT, with no ANYRANGE).
+ */
+ if (!OidIsValid(elem_typeid))
+ {
+ elem_typeid = get_range_subtype(range_typeid);
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
+ }
+
if (!OidIsValid(elem_typeid))
{
if (allow_poly)
@@ -1856,6 +1990,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -1928,6 +2063,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -1959,6 +2105,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
@@ -2007,8 +2169,7 @@ resolve_generic_type(Oid declared_type,
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
- context_declared_type == ANYENUMOID ||
- context_declared_type == ANYRANGEOID)
+ context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
@@ -2020,11 +2181,37 @@ resolve_generic_type(Oid declared_type,
format_type_be(context_actual_type))));
return array_typeid;
}
+ else if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid range_typelem = get_range_subtype(context_base_type);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+ Oid range_typelem = get_range_subtype(multirange_typelem);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
- declared_type == ANYENUMOID ||
- declared_type == ANYRANGEOID)
+ declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
@@ -2052,6 +2239,19 @@ resolve_generic_type(Oid declared_type,
"anyrange", format_type_be(context_base_type))));
return range_typelem;
}
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ /* Use the element type corresponding to actual type */
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
@@ -2060,6 +2260,74 @@ resolve_generic_type(Oid declared_type,
return context_actual_type;
}
}
+ else if (declared_type == ANYRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
+ else
+ {
+ /* We can't infer a range from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
+ else if (declared_type == ANYMULTIRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typeid = get_range_multirange(context_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return multirange_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else
+ {
+ /* We can't infer a multirange from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
else
{
/* declared_type isn't polymorphic, so return it as-is */
@@ -2174,6 +2442,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 580043233b..92967939f9 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -18,7 +18,7 @@ OBJS = acl.o amutils.o arrayfuncs.o array_expanded.o array_selfuncs.o \
geo_ops.o geo_selfuncs.o geo_spgist.o inet_cidr_ntop.o inet_net_pton.o \
int.o int8.o json.o jsonb.o jsonb_gin.o jsonb_op.o jsonb_util.o \
jsonfuncs.o jsonpath_gram.o jsonpath.o jsonpath_exec.o \
- like.o like_support.o lockfuncs.o mac.o mac8.o misc.o name.o \
+ like.o like_support.o lockfuncs.o mac.o mac8.o misc.o multirangetypes.o name.o \
network.o network_gist.o network_selfuncs.o network_spgist.o \
numeric.o numutils.o oid.o oracle_compat.o \
orderedsetaggs.o partitionfuncs.o pg_locale.o pg_lsn.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..5323a6a635
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,966 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/hashutils.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ Oid typiofunc; /* range type's I/O function */
+ Oid typioparam; /* range type's I/O parameter */
+ FmgrInfo proc; /* lookup result for typiofunc */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData * get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks either either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ Oid rngtypoid;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+ rngtypoid = rangetyp->type_id;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */ ;
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **) repalloc(ranges,
+ range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(
+ InputFunctionCall(&cache->proc, range_str_copy,
+ cache->typioparam, typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "Unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->proc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(
+ ReceiveFunctionCall(&cache->proc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range = RangeTypePGetDatum(ranges[i]);
+ range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
+ uint32 range_len = VARSIZE(range) - VARHDRSZ;
+ char *range_data = VARDATA(range);
+
+ pq_sendint32(buf, range_len);
+ pq_sendbytes(buf, range_data, range_len);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &cache->typiofunc);
+
+ if (!OidIsValid(cache->typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(cache->typiofunc, &cache->proc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ RangeType *range;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ bytelen += MAXALIGN(VARSIZE(range));
+ }
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ memcpy(ptr, range, VARSIZE(range));
+ ptr += MAXALIGN(VARSIZE(range));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of any ranges you like into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ * Returns the number of slots actually used,
+ * which may be less than input_range_count but never more.
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType * multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR, (errmsg("Can't construct multirange with a multi-dimensional array")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL element")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ ereport(ERROR, (errmsg("Zero-param multirange constructor shouldn't have arguments")));
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 5c886cfe96..94484b3373 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -185,6 +186,30 @@ anyrange_out(PG_FUNCTION_ARGS)
}
/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
+/*
* void_in - input routine for pseudo-type VOID.
*
* We allow this so that PL functions can return VOID without any special
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index e5c7e5c7ee..1feebc48dc 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -1177,12 +1175,68 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
+ return cmp;
+}
+
+/* btree comparator */
+Datum
+range_cmp(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int cmp;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ cmp = range_cmp_internal(typcache, r1, r2);
+
PG_FREE_IF_COPY(r1, 0);
PG_FREE_IF_COPY(r2, 1);
PG_RETURN_INT32(cmp);
}
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ /* For b-tree use, empty ranges sort before all else */
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
/* inequality operators using the range_cmp function */
Datum
range_lt(PG_FUNCTION_ARGS)
@@ -1218,13 +1272,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1284,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1325,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1354,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1392,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1938,6 +2006,45 @@ range_cmp_bound_values(TypeCacheEntry *typcache, RangeBound *b1,
}
/*
+ * Compares two ranges so we can qsort them.
+ * This expects that you give qsort a RangeType **,
+ * so the RangeTypes can be in diverse locations,
+ * as long as you have a list of pointers to them all.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
+/*
* Build an empty range value of the type indicated by the typcache entry.
*/
RangeType *
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 27602fa49c..360a71427e 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2469,6 +2469,16 @@ type_is_range(Oid typid)
}
/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
+/*
* get_type_category_preferred
*
* Given the type OID, fetch its category and preferred-type status.
@@ -3150,6 +3160,58 @@ get_range_subtype(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->mltrngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/* ---------- PG_MULTIRANGE CACHE ---------- */
+
+/*
+ * get_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_multirange_subtype(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 16297a52a1..3f0e7f8576 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -508,6 +508,17 @@ static const struct cachedesc cacheinfo[] = {
},
4
},
+ {RangeRelationId, /* MULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_mltrngtypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{NamespaceRelationId, /* NAMESPACENAME */
NamespaceNameIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 11920db0d9..0630687b49 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -278,6 +278,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -294,6 +295,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -500,8 +504,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -538,7 +542,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -563,7 +567,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -588,7 +592,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -640,6 +644,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -684,6 +695,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -759,6 +777,16 @@ lookup_type_cache(Oid type_id, int flags)
}
/*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
+ /*
* If requested, get information about a domain type
*/
if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
@@ -871,6 +899,33 @@ load_rangetype_info(TypeCacheEntry *typentry)
/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Form_pg_range pg_range;
+ HeapTuple tup;
+ Oid rangetypeOid;
+
+ /* get information from pg_range */
+ tup = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(typentry->type_id));
+ /* should not fail, since we already checked typtype ... */
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+ pg_range = (Form_pg_range) GETSTRUCT(tup);
+
+ rangetypeOid = pg_range->rngtypid;
+
+ ReleaseSysCache(tup);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
+
+
+/*
* load_domaintype_info --- helper routine to set up domain constraint info
*
* Note: we assume we're called in a relatively short-lived context, so it's
@@ -1469,11 +1524,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1516,6 +1571,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index b7fac5d295..e6e82fda63 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -438,11 +438,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anynonarray = false;
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
Oid anycollation = InvalidOid;
int i;
@@ -468,12 +470,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
case ANYRANGEOID:
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_anymultirange_result = true;
+ break;
default:
break;
}
}
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/*
@@ -501,6 +506,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (!OidIsValid(anyrange_type))
anyrange_type = get_call_expr_argtype(call_expr, i);
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(anymultirange_type))
+ anymultirange_type = get_call_expr_argtype(call_expr, i);
+ break;
default:
break;
}
@@ -508,7 +517,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/* If nothing found, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -529,19 +538,122 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ Oid rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ anymultirange_type = mltrngtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
/* Enforce ANYNONARRAY if needed */
if (have_anynonarray && type_is_array(anyelement_type))
@@ -608,6 +720,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
default:
break;
}
@@ -632,9 +752,11 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
int inargno;
int i;
@@ -693,6 +815,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ have_anymultirange_result = true;
+ else
+ {
+ if (!OidIsValid(anymultirange_type))
+ {
+ anymultirange_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anymultirange_type))
+ return false;
+ }
+ argtypes[i] = anymultirange_type;
+ }
+ break;
default:
break;
}
@@ -702,12 +839,12 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
/* Done? */
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/* If no input polymorphics, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -728,19 +865,122 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ Oid rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ }
+ else
+ {
+ return false;
+ }
+ }
/* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */
@@ -760,6 +1000,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = anymultirange_type;
+ break;
default:
break;
}
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 23cf481e78..50b4c4428f 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -137,8 +137,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index e312c8f194..6f41eda45c 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201909214
+#define CATALOG_VERSION_NO 201909215
#endif
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ef4445b017..ca9890ab6c 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -330,6 +330,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_mltrngtypid_index, 8001, on pg_range using btree(mltrngtypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 232557ee81..999493f94d 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 5e705019b4..b7cb2cb000 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -225,6 +225,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
@@ -385,6 +387,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index 2d575102ef..2e59cf9159 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index fa7dc96ece..4d319bb5b3 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3289,5 +3289,38 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'contsel', oprjoin => 'contjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 41e40d657a..536c10cb0c 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 5e69deeb7a..db1bef821f 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9702,6 +9702,83 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index dd9baa2678..9fd781e7b5 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ mltrngtypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', mltrngtypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', mltrngtypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', mltrngtypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ mltrngtypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ mltrngtypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index b01a074d09..9cfc56d4d2 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -45,6 +45,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* subtype difference as a float8, or 0 */
regproc rngsubdiff BKI_LOOKUP(pg_proc);
+
+ /* OID of the range's multirange type */
+ Oid mltrngtypid BKI_LOOKUP(pg_type);
} FormData_pg_range;
/* ----------------
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index be49e00114..c95a38849a 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -486,6 +486,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -594,5 +628,10 @@
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 2a584b4b13..6a25fcc9bf 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -259,6 +259,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -270,6 +271,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPCATEGORY_ENUM 'E'
#define TYPCATEGORY_GEOMETRIC 'G'
#define TYPCATEGORY_NETWORK 'I' /* think INET */
+#define TYPCATEGORY_MULTIRANGE 'M'
#define TYPCATEGORY_NUMERIC 'N'
#define TYPCATEGORY_PSEUDOTYPE 'P'
#define TYPCATEGORY_RANGE 'R'
@@ -285,7 +287,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#endif /* EXPOSE_TO_CLIENT_CODE */
@@ -344,4 +347,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index c8df5bff9f..ffdd90d69c 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -154,6 +154,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -179,6 +180,8 @@ extern void free_attstatsslot(AttStatsSlot *sslot);
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..ed2e19aafa
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,72 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ char vl_len_[4]; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType * range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType * make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType * make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 580e476501..6323034548 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 918765cc99..32603eeb58 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -66,6 +66,7 @@ enum SysCacheIdentifier
INDEXRELID,
LANGNAME,
LANGOID,
+ MULTIRANGETYPE,
NAMESPACENAME,
NAMESPACEOID,
OPERNAMENSP,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index 04bf28180d..eda39881ef 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -99,6 +99,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
/*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype;
+
+ /*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
*/
@@ -141,6 +146,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 0c24ba692d..0012901432 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -513,6 +513,8 @@ do_compile(FunctionCallInfo fcinfo,
rettypeid = INT4ARRAYOID;
else if (rettypeid == ANYRANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2496,6 +2498,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYRANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..5749537635
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,292 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index c19740e5db..916e1016b5 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -206,6 +213,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -224,6 +233,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -329,13 +340,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
@@ -343,16 +355,19 @@ ORDER BY 2;
2502 | anyarray_recv
2312 | anyelement_in
3504 | anyenum_in
+ 8002 | anymultirange_in
2777 | anynonarray_in
3832 | anyrange_in
750 | array_in
2400 | array_recv
3506 | enum_in
3532 | enum_recv
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(13 rows)
+(16 rows)
-- Look for functions that accept cstring and are neither datatype input
-- functions nor encoding conversion functions. It's almost never a good
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index cd7fc03b04..8ef9595c37 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -193,18 +193,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -238,17 +239,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -316,18 +318,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -361,17 +364,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -629,3 +633,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.mltrngtypid
+FROM pg_range p1
+WHERE p1.mltrngtypid IS NULL OR p1.mltrngtypid = 0;
+ rngtypid | rngsubtype | mltrngtypid
+----------+------------+-------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index fc0f14122b..c8c1cd0671 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 68ac56acdb..35155436f0 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..8651ba3f3d
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,65 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 624bea46ce..23932cfef0 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -273,13 +284,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- Look for functions that accept cstring and are neither datatype input
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fed413bf98..b102c01bbc 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -151,7 +151,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -180,7 +180,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -232,7 +232,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -261,7 +261,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -465,3 +465,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.mltrngtypid
+FROM pg_range p1
+WHERE p1.mltrngtypid IS NULL OR p1.mltrngtypid = 0;
--
2.11.0
[application/octet-stream] v3-0002-multirange-operators-and-functions.patch (164.5K, ../../CA+renyWOSQ5P-=KCqti4WN1hdS9h9O5kFSvXqodq49XaR43HkA@mail.gmail.com/5-v3-0002-multirange-operators-and-functions.patch)
download | inline diff:
From 759c5f4647cd75932aac57b00f6db06707a2ca06 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:20 -0700
Subject: [PATCH v3 2/4] multirange operators and functions
---
src/backend/utils/adt/multirangetypes.c | 1422 ++++++++++++++-
src/backend/utils/adt/rangetypes.c | 230 ++-
src/include/catalog/pg_aggregate.dat | 11 +
src/include/catalog/pg_amproc.dat | 5 +-
src/include/catalog/pg_operator.dat | 169 ++
src/include/catalog/pg_proc.dat | 185 ++
src/include/utils/multirangetypes.h | 34 +
src/include/utils/rangetypes.h | 24 +-
src/test/regress/expected/dependency.out | 1 +
src/test/regress/expected/multirangetypes.out | 2397 +++++++++++++++++++++++++
src/test/regress/expected/opr_sanity.out | 4 +-
src/test/regress/expected/rangetypes.out | 38 +-
src/test/regress/expected/sanity_check.out | 2 +
src/test/regress/sql/multirangetypes.sql | 607 +++++++
src/test/regress/sql/rangetypes.sql | 13 +
15 files changed, 5041 insertions(+), 101 deletions(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 5323a6a635..c509994796 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -726,45 +726,1267 @@ multirange_constructor0(PG_FUNCTION_ARGS)
}
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+range_union_range(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ MultirangeType *mr = make_multirange(mltrngtypoid, typcache->rngtype, 1, &r2);
+
+ PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r1, mr));
+}
+
+Datum
+range_union_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_union_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_union_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
+}
+
+MultirangeType *
+range_union_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (RangeIsEmpty(r))
+ return mr;
+ if (MultirangeIsEmpty(mr))
+ return make_multirange(typcache->type_id, typcache->rngtype, 1, &r);
+
+ multirange_deserialize(mr, &range_count, &ranges1);
+
+ ranges2 = palloc0((range_count + 1) * sizeof(RangeType *));
+
+ memcpy(ranges2, ranges1, range_count * sizeof(RangeType *));
+ ranges2[range_count] = r;
+ return make_multirange(typcache->type_id, typcache->rngtype, range_count + 1, ranges2);
+}
+
+/* multirange minus */
+Datum
+range_minus_range(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r1) || RangeIsEmpty(r2))
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, rangetyp, 1, &r1));
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r1,
+ 1,
+ &r2));
+}
+
+Datum
+range_minus_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, rangetyp, 1, &r));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r,
+ range_count,
+ ranges));
+}
+
+Datum
+multirange_minus_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr) || RangeIsEmpty(r))
+ PG_RETURN_MULTIRANGE_P(mr);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count,
+ ranges,
+ 1,
+ &r));
+}
+
+Datum
+multirange_minus_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1, int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+range_intersect_range(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r1) || RangeIsEmpty(r2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r1,
+ 1,
+ &r2));
+}
+
+Datum
+range_intersect_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr2);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count2;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r1,
+ range_count2,
+ ranges2));
+}
+
+Datum
+multirange_intersect_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ RangeType **ranges1;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || RangeIsEmpty(r2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ 1,
+ &r2));
+}
+
+Datum
+multirange_intersect_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1, int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1, but one extra won't
+ * hurt.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_multirange_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
/* multirange, multirange -> bool functions */
-/* equality (internal version) */
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType * mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges1[0], ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
bool
-multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1, MultirangeType * mr2)
{
- int32 range_count_1;
- int32 range_count_2;
- int32 i;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
RangeType **ranges1;
RangeType **ranges2;
RangeType *r1;
RangeType *r2;
+ int i1,
+ i2;
- /* Different types should be prevented by ANYMULTIRANGE matching rules */
- if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
- elog(ERROR, "multirange types do not match");
+ rangetyp = typcache->rngtype;
- multirange_deserialize(mr1, &range_count_1, &ranges1);
- multirange_deserialize(mr2, &range_count_2, &ranges2);
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
- if (range_count_1 != range_count_2)
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
return false;
- for (i = 0; i < range_count_1; i++)
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
{
- r1 = ranges1[i];
- r2 = ranges2[i];
+ r2 = ranges2[i2];
- if (!range_eq_internal(typcache->rngtype, r1, r2))
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
return false;
}
+ /* All ranges in mr2 are satisfied */
return true;
}
-/* equality */
+/* strictly left of? */
Datum
-multirange_eq(PG_FUNCTION_ARGS)
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
{
MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
@@ -772,27 +1994,156 @@ multirange_eq(PG_FUNCTION_ARGS)
typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
- PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
}
-/* inequality (internal version) */
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
bool
-multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
{
- return (!multirange_eq_internal(typcache, mr1, mr2));
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ int32 range_count;
+ RangeType **ranges;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
}
-/* inequality */
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ int32 range_count;
+ RangeType **ranges;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
Datum
-multirange_ne(PG_FUNCTION_ARGS)
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
{
MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
- PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
}
/* Btree support */
@@ -891,6 +2242,29 @@ multirange_gt(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(cmp > 0);
}
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
/* Hash support */
/* hash a multirange value */
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 1feebc48dc..bd9ad67672 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -429,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -450,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -473,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -483,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -493,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -503,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -513,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -955,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -967,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -991,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1099,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1108,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1130,54 +1180,83 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
}
-/* Btree support */
+/* range, range -> range, range functions */
-/* btree comparator */
-Datum
-range_cmp(PG_FUNCTION_ARGS)
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
+ RangeType **output1, RangeType **output2)
{
- RangeType *r1 = PG_GETARG_RANGE_P(0);
- RangeType *r2 = PG_GETARG_RANGE_P(1);
- TypeCacheEntry *typcache;
RangeBound lower1,
lower2;
RangeBound upper1,
upper2;
bool empty1,
empty2;
- int cmp;
-
- check_stack_depth(); /* recurses when subtype is a range type */
-
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- /* For b-tree use, empty ranges sort before all else */
- if (empty1 && empty2)
- cmp = 0;
- else if (empty1)
- cmp = -1;
- else if (empty2)
- cmp = 1;
- else
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
{
- cmp = range_cmp_bounds(typcache, &lower1, &lower2);
- if (cmp == 0)
- cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
}
- return cmp;
+ return false;
+}
+
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
}
+
+/* Btree support */
+
/* btree comparator */
Datum
range_cmp(PG_FUNCTION_ARGS)
@@ -1838,6 +1917,21 @@ range_get_flags(RangeType *range)
}
/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
+/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
* This is only needed in GiST operations, so we don't include a provision
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index 242d843347..07a7211c15 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index b7cb2cb000..32965d6b3d 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -1210,12 +1210,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 4d319bb5b3..ade223b6f3 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3322,5 +3322,174 @@
oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8138', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anyrange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@+(anyrange,anyrange)',
+ oprcode => 'range_union_range' },
+{ oid => '8115', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@+(anymultirange,anyrange)',
+ oprcode => 'range_union_multirange' },
+{ oid => '8116', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@+(anyrange,anymultirange)',
+ oprcode => 'multirange_union_range' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union_multirange' },
+{ oid => '8140', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anyrange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcode => 'range_minus_range' },
+{ oid => '8121', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'range_minus_multirange' },
+{ oid => '8122', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus_range' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus_multirange' },
+{ oid => '8142', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anyrange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@*(anyrange,anyrange)',
+ oprcode => 'range_intersect_range' },
+{ oid => '8127', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@*(anymultirange,anyrange)',
+ oprcode => 'range_intersect_multirange' },
+{ oid => '8128', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@*(anyrange,anymultirange)',
+ oprcode => 'multirange_intersect_range' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect_multirange' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index db1bef821f..274a25027f 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9585,6 +9585,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9634,6 +9638,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9721,9 +9732,165 @@
{ oid => '8007', descr => 'I/O',
proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
{ oid => '8008', descr => 'multirange typanalyze',
proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8137',
+ proname => 'range_union_range', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_union_range' },
+{ oid => '8112',
+ proname => 'range_union_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_union_multirange' },
+{ oid => '8113',
+ proname => 'multirange_union_range', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_union_range' },
+{ oid => '8114',
+ proname => 'multirange_union_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union_multirange' },
+{ oid => '8139',
+ proname => 'range_minus_range', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_minus_range' },
+{ oid => '8118',
+ proname => 'range_minus_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_minus_multirange' },
+{ oid => '8119',
+ proname => 'multirange_minus_range', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_minus_range' },
+{ oid => '8120',
+ proname => 'multirange_minus_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus_multirange' },
+{ oid => '8141',
+ proname => 'range_intersect_range', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_range' },
+{ oid => '8124',
+ proname => 'range_intersect_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_intersect_multirange' },
+{ oid => '8125',
+ proname => 'multirange_intersect_range', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_intersect_range' },
+{ oid => '8126',
+ proname => 'multirange_intersect_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_multirange' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
{ oid => '8045', descr => 'int4multirange constructor',
proname => 'int4multirange', proisstrict => 'f',
@@ -9779,6 +9946,24 @@
prorettype => 'int8multirange', proargtypes => '_int8range',
proallargtypes => '{_int8range}', proargmodes => '{v}',
prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
index ed2e19aafa..cc4f82b0ba 100644
--- a/src/include/utils/multirangetypes.h
+++ b/src/include/utils/multirangetypes.h
@@ -59,6 +59,40 @@ extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr
MultirangeType * mr2);
extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
MultirangeType * mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern MultirangeType * range_union_multirange_internal(TypeCacheEntry *typcache,
+ RangeType *r,
+ MultirangeType * mr);
+extern MultirangeType * multirange_minus_multirange_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType * multirange_intersect_multirange_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
/* assorted support functions */
extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 6323034548..33775a9d42 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -94,13 +94,22 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, RangeType *r1,
RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, RangeType *r1,
RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, RangeType *r,
+ Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, RangeType *r1,
RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, RangeType *r1,
@@ -117,6 +126,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, RangeType *r1,
RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, RangeType *r1,
RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -127,6 +142,7 @@ extern void range_deserialize(TypeCacheEntry *typcache, RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(RangeType *range);
+extern bool range_has_flag(RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
@@ -134,8 +150,12 @@ extern int range_cmp_bounds(TypeCacheEntry *typcache, RangeBound *b1,
RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, RangeBound *b1,
RangeBound *b2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..778699a961 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -141,6 +141,7 @@ owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
owner of type deptest_range
+owner of type deptest_multirange
owner of table deptest2
owner of sequence ss1
owner of type deptest_t
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
index 5749537635..2276d6d002 100644
--- a/src/test/regress/expected/multirangetypes.out
+++ b/src/test/regress/expected/multirangetypes.out
@@ -290,3 +290,2400 @@ select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
{[a,d)}
(1 row)
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT 'empty'::numrange @+ 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @+ nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @+ 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @+ nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @+ numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT 'empty'::numrange @+ nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @+ numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @+ nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(2,4);
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(3,4);
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT 'empty'::numrange @- 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @- nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT 'empty'::numrange @- numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @- 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @- numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(1,3);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(1,2);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(2,4);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(3,4);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(1,3));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,10) @- nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------------
+ {[2,4),[5,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(0,2);
+ ?column?
+----------
+ {[2,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(1,2);
+ ?column?
+----------
+ {[2,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(3,4);
+ ?column?
+----------------
+ {[1,3),[4,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(13,18);
+ ?column?
+----------
+ {[1,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) @- nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) @- nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT 'empty'::numrange @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,3);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,5);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(2,5);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,5) @* numrange(2,3);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,3);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,5);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(2,5);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @* numrange(2,3);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,2) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,5) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(2,5) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(2,3) @* '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange @* '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange @* '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select $1 @+ $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 916e1016b5..e818eaea79 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1101,13 +1101,15 @@ ORDER BY 1, 2;
?| | ?|
?|| | ?||
@ | ~
+ @* | @*
+ @+ | @+
@@ | @@
@@@ | @@@
| | |
~<=~ | ~>=~
~<~ | ~>~
~= | ~=
-(30 rows)
+(32 rows)
-- Likewise for negator pairs.
SELECT DISTINCT o1.oprname AS op1, o2.oprname AS op2
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 60d875e898..d825c0db1a 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1284,7 +1302,7 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
@@ -1392,6 +1410,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1400,6 +1419,16 @@ select * from outparam_succeed(int4range(1,2));
[1,2) | foo
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1408,6 +1437,7 @@ select * from inoutparam_succeed(int4range(1,2));
2 | [1,2)
(1 row)
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
select * from table_succeed(123, int4range(1,11));
@@ -1420,14 +1450,14 @@ select * from table_succeed(123, int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index d6e75ffce6..3d92541ebc 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
onek|t
onek2|t
path_tbl|f
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
index 8651ba3f3d..b3ede18f96 100644
--- a/src/test/regress/sql/multirangetypes.sql
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -63,3 +63,610 @@ select textmultirange();
select textmultirange(textrange('a', 'c'));
select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT 'empty'::numrange @+ 'empty'::numrange;
+SELECT 'empty'::numrange @+ nummultirange();
+SELECT nummultirange() @+ 'empty'::numrange;
+SELECT nummultirange() @+ nummultirange();
+SELECT 'empty'::numrange @+ numrange(1,2);
+SELECT 'empty'::numrange @+ nummultirange(numrange(1,2));
+SELECT numrange(1,2) @+ nummultirange();
+SELECT nummultirange(numrange(1,2)) @+ 'empty'::numrange;
+SELECT numrange(1,2) @+ 'empty'::numrange;
+SELECT nummultirange() @+ numrange(1,2);
+SELECT nummultirange() @+ nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange();
+SELECT numrange(1,2) @+ numrange(1,2);
+SELECT numrange(1,2) @+ numrange(2,4);
+SELECT numrange(1,2) @+ numrange(3,4);
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT 'empty'::numrange @- 'empty'::numrange;
+SELECT 'empty'::numrange @- nummultirange();
+SELECT nummultirange() @- 'empty'::numrange;
+SELECT nummultirange() @- nummultirange();
+SELECT 'empty'::numrange @- numrange(1,2);
+SELECT 'empty'::numrange @- nummultirange(numrange(1,2));
+SELECT numrange(1,2) @- nummultirange();
+SELECT nummultirange(numrange(1,2)) @- 'empty'::numrange;
+SELECT 'empty'::numrange @- numrange(1,2);
+SELECT numrange(1,2) @- 'empty'::numrange;
+SELECT nummultirange() @- numrange(1,2);
+SELECT nummultirange() @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @- nummultirange();
+SELECT numrange(1,3) @- numrange(1,3);
+SELECT numrange(1,3) @- numrange(1,2);
+SELECT numrange(1,3) @- numrange(2,4);
+SELECT numrange(1,3) @- numrange(3,4);
+SELECT numrange(1,3) @- nummultirange(numrange(1,3));
+SELECT numrange(1,3) @- nummultirange(numrange(1,2));
+SELECT numrange(1,3) @- nummultirange(numrange(2,4));
+SELECT numrange(1,3) @- nummultirange(numrange(3,4));
+SELECT numrange(1,10) @- nummultirange(numrange(1,2), numrange(4,5));
+SELECT nummultirange(numrange(1,10)) @- numrange(0,2);
+SELECT nummultirange(numrange(1,10)) @- numrange(1,2);
+SELECT nummultirange(numrange(1,10)) @- numrange(3,4);
+SELECT nummultirange(numrange(1,10)) @- numrange(13,18);
+SELECT nummultirange(numrange(1,2), numrange(3,4)) @- nummultirange();
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) @- nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT 'empty'::numrange @* 'empty'::numrange;
+SELECT 'empty'::numrange @* nummultirange();
+SELECT nummultirange() @* 'empty'::numrange;
+SELECT nummultirange() @* nummultirange();
+SELECT 'empty'::numrange @* numrange(1,2);
+SELECT 'empty'::numrange @* nummultirange(numrange(1,2));
+SELECT numrange(1,2) @* 'empty'::numrange;
+SELECT numrange(1,2) @* nummultirange();
+SELECT nummultirange(numrange(1,2)) @* 'empty'::numrange;
+SELECT nummultirange() @* numrange(1,2);
+SELECT nummultirange() @* nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @* nummultirange();
+SELECT numrange(1,3) @* numrange(1,3);
+SELECT numrange(1,3) @* numrange(1,2);
+SELECT numrange(1,3) @* numrange(1,5);
+SELECT numrange(1,3) @* numrange(2,5);
+SELECT numrange(1,5) @* numrange(2,3);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,3);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,2);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,5);
+SELECT '{[1,3)}'::nummultirange @* numrange(2,5);
+SELECT '{[1,5)}'::nummultirange @* numrange(2,3);
+SELECT numrange(1,3) @* '{[1,3)}'::nummultirange;
+SELECT numrange(1,2) @* '{[1,3)}'::nummultirange;
+SELECT numrange(1,5) @* '{[1,3)}'::nummultirange;
+SELECT numrange(2,5) @* '{[1,3)}'::nummultirange;
+SELECT numrange(2,3) @* '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange @* '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange @* '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select $1 @+ $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 9fdb1953df..d23855d08e 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 (nr);
@@ -481,16 +485,25 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
--
2.11.0
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-09-26 21:13 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Alvaro Herrera @ 2019-09-26 21:13 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
Hello Paul, I've started to review this patch. Here's a few minor
things I ran across -- mostly compiler warnings (is my compiler too
ancient?). You don't have to agree with every fix -- feel free to use
different fixes if you have them. Also, feel free to squash them onto
whatever commit you like (I think they all belong onto 0001 except the
last which seems to be for 0002).
Did you not push your latest version to your github repo? I pulled from
there and branch 'multirange' does not seem to match what you posted.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
[text/x-diff] 0001-Remove-unused-variable.patch (1.1K, ../../20190926211349.GA3375@alvherre.pgsql/2-0001-Remove-unused-variable.patch)
download | inline diff:
From 6d849beafea1e4129f044f8dd038933d6ea72b54 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 26 Sep 2019 16:56:26 -0300
Subject: [PATCH 1/9] Remove unused variable
---
src/backend/catalog/pg_type.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 08552850c8..ea5b161c4d 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -876,7 +876,6 @@ makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
char mltrng[NAMEDATALEN];
char *mltrngunique = (char *) palloc(NAMEDATALEN);
int namelen = strlen(rangeTypeName);
- int rangelen;
char *rangestr;
int rangeoffset;
int underscores;
@@ -892,7 +891,6 @@ makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
if (rangestr)
{
rangeoffset = rangestr - rangeTypeName;
- rangelen = strlen(rangestr);
strlcpy(mltrng + rangeoffset, "multi", NAMEDATALEN - rangeoffset);
strlcpy(mltrng + rangeoffset + 5, rangestr, NAMEDATALEN - rangeoffset - 5);
namelen += 5;
--
2.17.1
[text/x-diff] 0002-Silence-compiler-warning.patch (777B, ../../20190926211349.GA3375@alvherre.pgsql/3-0002-Silence-compiler-warning.patch)
download | inline diff:
From 63bd9723a45c33cccd11704817fd954af5abaf31 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 26 Sep 2019 17:39:54 -0300
Subject: [PATCH 2/9] Silence compiler warning
---
src/backend/parser/parse_coerce.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index f0b1f6f831..aab2348952 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1919,6 +1919,8 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(elem_typeid))));
}
}
+ else
+ range_typelem = InvalidOid;
/* Get the range type based on the multirange type, if we have one */
if (OidIsValid(multirange_typeid))
--
2.17.1
[text/x-diff] 0003-Silence-mixed-declarations-and-code-compiler-warning.patch (1.4K, ../../20190926211349.GA3375@alvherre.pgsql/4-0003-Silence-mixed-declarations-and-code-compiler-warning.patch)
download | inline diff:
From c4e4b1a97136f7a2047f8be6608cac0782dd6d12 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 26 Sep 2019 17:42:22 -0300
Subject: [PATCH 3/9] Silence 'mixed declarations and code' compiler warning
---
src/backend/commands/typecmds.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 14a6857062..26ed3e4c76 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1782,14 +1782,10 @@ makeMultirangeConstructors(const char *name, Oid namespace,
static const char *const prosrc[2] = {"multirange_constructor0",
"multirange_constructor1"};
static const int pronargs[2] = {0, 1};
-
- Oid constructorArgTypes[0];
+ Oid constructorArgTypes = rangeArrayOid;
ObjectAddress myself,
referenced;
int i;
-
- constructorArgTypes[0] = rangeArrayOid;
-
Datum allParamTypes[1] = {ObjectIdGetDatum(rangeArrayOid)};
ArrayType *allParameterTypes = construct_array(allParamTypes, 1, OIDOID,
sizeof(Oid), true, 'i');
@@ -1808,7 +1804,7 @@ makeMultirangeConstructors(const char *name, Oid namespace,
{
oidvector *constructorArgTypesVector;
- constructorArgTypesVector = buildoidvector(constructorArgTypes,
+ constructorArgTypesVector = buildoidvector(&constructorArgTypes,
pronargs[i]);
myself = ProcedureCreate(name, /* name: same as multirange type */
--
2.17.1
[text/x-diff] 0004-Silence-mixed-declarations-and-code-compiler-warning.patch (6.5K, ../../20190926211349.GA3375@alvherre.pgsql/5-0004-Silence-mixed-declarations-and-code-compiler-warning.patch)
download | inline diff:
From af414286417d4fddd078c7ffac087482b3ba959d Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 26 Sep 2019 17:49:19 -0300
Subject: [PATCH 4/9] Silence 'mixed declarations and code' compiler warnings
---
src/backend/utils/fmgr/funcapi.c | 98 +++++++++++++++++++-------------
1 file changed, 59 insertions(+), 39 deletions(-)
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index e6e82fda63..701664bd3b 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -541,18 +541,21 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
- anyrange_type = rngtype;
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ anyrange_type = rngtype;
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -596,18 +599,21 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
{
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
anyrange_type = rngtype;
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -628,21 +634,25 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
{
if (OidIsValid(anyrange_type))
{
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and range results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
return false;
anyelement_type = subtype;
- Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
- anyrange_type,
- ANYRANGEOID);
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent range and multirange results */
- Oid rngtype = get_multirange_subtype(mltrngtype);
+ rngtype = get_multirange_subtype(mltrngtype);
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
@@ -868,18 +878,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
anyrange_type = rngtype;
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -923,18 +936,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
{
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
anyrange_type = rngtype;
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -955,21 +971,25 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
{
if (OidIsValid(anyrange_type))
{
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and range results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
return false;
anyelement_type = subtype;
- Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
- anyrange_type,
- ANYRANGEOID);
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent range and multirange results */
- Oid rngtype = get_multirange_subtype(mltrngtype);
+ rngtype = get_multirange_subtype(mltrngtype);
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
--
2.17.1
[text/x-diff] 0005-Silence-mixed-declarations-and-code-compiler-warning.patch (2.4K, ../../20190926211349.GA3375@alvherre.pgsql/6-0005-Silence-mixed-declarations-and-code-compiler-warning.patch)
download | inline diff:
From 43c82181760ad982cb6958817f34cb80f39ffd8c Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 26 Sep 2019 17:51:02 -0300
Subject: [PATCH 5/9] Silence 'mixed declarations and code' compiler warnings
---
src/backend/utils/adt/multirangetypes.c | 25 ++++++++++++++-----------
1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index c509994796..359b78d056 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -351,9 +351,12 @@ multirange_send(PG_FUNCTION_ARGS)
for (i = 0; i < range_count; i++)
{
Datum range = RangeTypePGetDatum(ranges[i]);
+ uint32 range_len;
+ char *range_data;
+
range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
- uint32 range_len = VARSIZE(range) - VARHDRSZ;
- char *range_data = VARDATA(range);
+ range_len = VARSIZE(range) - VARHDRSZ;
+ *range_data = VARDATA(range);
pq_sendint32(buf, range_len);
pq_sendbytes(buf, range_data, range_len);
@@ -2039,12 +2042,12 @@ bool
range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
MultirangeType * mr)
{
- if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
- return false;
-
int32 range_count;
RangeType **ranges;
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
multirange_deserialize(mr, &range_count, &ranges);
return range_before_internal(typcache->rngtype, r, ranges[0]);
@@ -2054,14 +2057,14 @@ bool
multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
MultirangeType * mr2)
{
- if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
- return false;
-
int32 range_count1;
int32 range_count2;
RangeType **ranges1;
RangeType **ranges2;
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
multirange_deserialize(mr1, &range_count1, &ranges1);
multirange_deserialize(mr2, &range_count2, &ranges2);
@@ -2074,12 +2077,12 @@ bool
range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
MultirangeType * mr)
{
- if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
- return false;
-
int32 range_count;
RangeType **ranges;
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
multirange_deserialize(mr, &range_count, &ranges);
return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
--
2.17.1
[text/x-diff] 0006-silence-variable-set-but-not-used-compiler-warning.patch (1.1K, ../../20190926211349.GA3375@alvherre.pgsql/7-0006-silence-variable-set-but-not-used-compiler-warning.patch)
download | inline diff:
From a735dadde028785020b71e8aac40fc717f6c765c Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 26 Sep 2019 17:51:33 -0300
Subject: [PATCH 6/9] silence 'variable set but not used' compiler warning
---
src/backend/utils/adt/multirangetypes.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 359b78d056..cea7e7f97e 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -91,7 +91,6 @@ multirange_in(PG_FUNCTION_ARGS)
Oid mltrngtypoid = PG_GETARG_OID(1);
Oid typmod = PG_GETARG_INT32(2);
TypeCacheEntry *rangetyp;
- Oid rngtypoid;
int32 ranges_seen = 0;
int32 range_count = 0;
int32 range_capacity = 8;
@@ -107,7 +106,6 @@ multirange_in(PG_FUNCTION_ARGS)
cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
rangetyp = cache->typcache->rngtype;
- rngtypoid = rangetyp->type_id;
/* consume whitespace */
while (*ptr != '\0' && isspace((unsigned char) *ptr))
--
2.17.1
[text/x-diff] 0007-Fix-uninitialized-variable-warning.patch (798B, ../../20190926211349.GA3375@alvherre.pgsql/8-0007-Fix-uninitialized-variable-warning.patch)
download | inline diff:
From 670f80824c92dd9b36f823110182339dd8fc85b8 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 26 Sep 2019 18:00:29 -0300
Subject: [PATCH 7/9] Fix uninitialized variable warning
---
src/backend/utils/adt/multirangetypes.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index cea7e7f97e..ba7c2a23ae 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -100,7 +100,7 @@ multirange_in(PG_FUNCTION_ARGS)
MultirangeType *ret;
MultirangeParseState parse_state;
const char *ptr = input_str;
- const char *range_str;
+ const char *range_str = NULL;
int32 range_str_len;
char *range_str_copy;
--
2.17.1
[text/x-diff] 0008-Fix-wrong-type-assignment-warning.patch (855B, ../../20190926211349.GA3375@alvherre.pgsql/9-0008-Fix-wrong-type-assignment-warning.patch)
download | inline diff:
From edc5171168c804fe6127cb0ce51c5fad9a6cbb46 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 26 Sep 2019 18:00:44 -0300
Subject: [PATCH 8/9] Fix wrong-type assignment warning
---
src/backend/utils/adt/multirangetypes.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index ba7c2a23ae..c85f5dc90d 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -354,7 +354,7 @@ multirange_send(PG_FUNCTION_ARGS)
range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
range_len = VARSIZE(range) - VARHDRSZ;
- *range_data = VARDATA(range);
+ range_data = VARDATA(range);
pq_sendint32(buf, range_len);
pq_sendbytes(buf, range_data, range_len);
--
2.17.1
[text/x-diff] 0009-Fix-crash-in-multirange_intersect_multirange_interna.patch (1.3K, ../../20190926211349.GA3375@alvherre.pgsql/10-0009-Fix-crash-in-multirange_intersect_multirange_interna.patch)
download | inline diff:
From 1479908cd0df9cc77968dbb0def484f59ad9d64c Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 26 Sep 2019 18:01:04 -0300
Subject: [PATCH 9/9] Fix crash in multirange_intersect_multirange_internal
---
src/backend/utils/adt/multirangetypes.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index c85f5dc90d..7717acb3b1 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -1123,7 +1123,8 @@ multirange_intersect_multirange(PG_FUNCTION_ARGS)
MultirangeType *
multirange_intersect_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
- int32 range_count1, RangeType **ranges1, int32 range_count2, RangeType **ranges2)
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
{
RangeType *r1;
RangeType *r2;
@@ -1132,6 +1133,9 @@ multirange_intersect_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *range
int32 i1;
int32 i2;
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
/*
* Worst case is a stitching pattern like this:
*
--
2.17.1
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2019-11-06 23:02 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2019-11-06 23:02 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Thu, Sep 26, 2019 at 2:13 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
>
> Hello Paul, I've started to review this patch. Here's a few minor
> things I ran across -- mostly compiler warnings (is my compiler too
> ancient?). You don't have to agree with every fix -- feel free to use
> different fixes if you have them. Also, feel free to squash them onto
> whatever commit you like (I think they all belong onto 0001 except the
> last which seems to be for 0002).
Hi Alvaro, sorry, I missed your note from September. I really
appreciate your review and will take a look at your suggested changes!
I just opened this thread to post a rebased set patches (especially
because of the `const` additions to range functions). Maybe it's not
that helpful since they don't include your changes yet but here they
are anyway. I'll post some more with your changes shortly.
> Did you not push your latest version to your github repo? I pulled from
> there and branch 'multirange' does not seem to match what you posted.
Hmm, I'll take a look. Before I made the v3 files I switched to
multirange-patch so I could squash things and use git to generate one
patch file per commit. So `multirange` isn't rebased as currently as
`multirange-patch`. If you don't mind a force-push I can update
`multirange` to be the same.
Yours,
Paul
Attachments:
[application/octet-stream] v4-0001-multirange-type-basics.patch (140.5K, ../../CA+renyWY-vfLVM8NkHj11xXyCNYJA7=1sd5_Bk+B+DL66y6-WQ@mail.gmail.com/2-v4-0001-multirange-type-basics.patch)
download | inline diff:
From fcf6446aff14bda7128995ee72eb8942420cefc6 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:25:39 -0700
Subject: [PATCH v4 1/4] multirange type basics
---
src/backend/catalog/pg_proc.c | 16 +-
src/backend/catalog/pg_range.c | 11 +-
src/backend/catalog/pg_type.c | 134 +++-
src/backend/commands/typecmds.c | 171 ++++-
src/backend/parser/parse_coerce.c | 301 +++++++-
src/backend/utils/adt/Makefile | 1 +
src/backend/utils/adt/multirangetypes.c | 966 ++++++++++++++++++++++++++
src/backend/utils/adt/pseudotypes.c | 25 +
src/backend/utils/adt/rangetypes.c | 155 ++++-
src/backend/utils/cache/lsyscache.c | 62 ++
src/backend/utils/cache/syscache.c | 11 +
src/backend/utils/cache/typcache.c | 109 ++-
src/backend/utils/fmgr/funcapi.c | 271 +++++++-
src/include/access/tupmacs.h | 4 +-
src/include/catalog/catversion.h | 2 +-
src/include/catalog/indexing.h | 3 +
src/include/catalog/pg_amop.dat | 22 +
src/include/catalog/pg_amproc.dat | 7 +
src/include/catalog/pg_opclass.dat | 4 +
src/include/catalog/pg_operator.dat | 33 +
src/include/catalog/pg_opfamily.dat | 4 +
src/include/catalog/pg_proc.dat | 77 ++
src/include/catalog/pg_range.dat | 15 +-
src/include/catalog/pg_range.h | 5 +-
src/include/catalog/pg_type.dat | 39 ++
src/include/catalog/pg_type.h | 8 +-
src/include/utils/lsyscache.h | 3 +
src/include/utils/multirangetypes.h | 72 ++
src/include/utils/rangetypes.h | 2 +
src/include/utils/syscache.h | 1 +
src/include/utils/typcache.h | 6 +
src/pl/plpgsql/src/pl_comp.c | 5 +
src/test/regress/expected/hash_func.out | 13 +
src/test/regress/expected/multirangetypes.out | 292 ++++++++
src/test/regress/expected/opr_sanity.out | 23 +-
src/test/regress/expected/type_sanity.out | 46 +-
src/test/regress/parallel_schedule | 3 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/hash_func.sql | 10 +
src/test/regress/sql/multirangetypes.sql | 65 ++
src/test/regress/sql/opr_sanity.sql | 18 +-
src/test/regress/sql/type_sanity.sql | 16 +-
42 files changed, 2895 insertions(+), 137 deletions(-)
create mode 100644 src/backend/utils/adt/multirangetypes.c
create mode 100644 src/include/utils/multirangetypes.h
create mode 100644 src/test/regress/expected/multirangetypes.out
create mode 100644 src/test/regress/sql/multirangetypes.sql
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index ef009ad2bc..80cc0721a5 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -192,6 +192,7 @@ ProcedureCreate(const char *procedureName,
genericInParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericInParam = true;
anyrangeInParam = true;
break;
@@ -219,6 +220,7 @@ ProcedureCreate(const char *procedureName,
genericOutParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericOutParam = true;
anyrangeOutParam = true;
break;
@@ -231,10 +233,10 @@ ProcedureCreate(const char *procedureName,
/*
* Do not allow polymorphic return type unless at least one input argument
- * is polymorphic. ANYRANGE return type is even stricter: must have an
- * ANYRANGE input (since we can't deduce the specific range type from
- * ANYELEMENT). Also, do not allow return type INTERNAL unless at least
- * one input argument is INTERNAL.
+ * is polymorphic. ANYRANGE and ANYMULTIRANGE return types are even
+ * stricter: must have an ANYRANGE or ANYMULTIRANGE input (since we can't
+ * deduce the specific range type from ANYELEMENT). Also, do not allow
+ * return type INTERNAL unless at least one input argument is INTERNAL.
*/
if ((IsPolymorphicType(returnType) || genericOutParam)
&& !genericInParam)
@@ -243,12 +245,12 @@ ProcedureCreate(const char *procedureName,
errmsg("cannot determine result data type"),
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
- if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
- !anyrangeInParam)
+ if ((returnType == ANYRANGEOID || returnType == ANYMULTIRANGEOID
+ || anyrangeOutParam) && !anyrangeInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
- errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
+ errdetail("A function returning \"anyrange\" or \"anymultirange\" must have at least one \"anyrange\" or \"anymultirange\" argument.")));
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
ereport(ERROR,
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index e6e138babd..d914f04858 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_mltrngtypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,13 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 2a51501d8d..08552850c8 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -36,6 +36,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static int makeUniqueTypeName(char *dest, const char *typeName, size_t namelen,
+ Oid typeNamespace, bool tryOriginalName);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -785,34 +788,10 @@ makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
char *arr = (char *) palloc(NAMEDATALEN);
int namelen = strlen(typeName);
- Relation pg_type_desc;
- int i;
-
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- pg_type_desc = table_open(TypeRelationId, AccessShareLock);
-
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
-
- table_close(pg_type_desc, AccessShareLock);
+ int underscores;
- if (i >= NAMEDATALEN - 1)
+ underscores = makeUniqueTypeName(arr, typeName, namelen, typeNamespace, false);
+ if (underscores >= NAMEDATALEN - 1)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -883,3 +862,104 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * the caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char mltrng[NAMEDATALEN];
+ char *mltrngunique = (char *) palloc(NAMEDATALEN);
+ int namelen = strlen(rangeTypeName);
+ int rangelen;
+ char *rangestr;
+ int rangeoffset;
+ int underscores;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ strlcpy(mltrng, rangeTypeName, NAMEDATALEN);
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ rangeoffset = rangestr - rangeTypeName;
+ rangelen = strlen(rangestr);
+ strlcpy(mltrng + rangeoffset, "multi", NAMEDATALEN - rangeoffset);
+ strlcpy(mltrng + rangeoffset + 5, rangestr, NAMEDATALEN - rangeoffset - 5);
+ namelen += 5;
+ }
+ else
+ {
+ strlcpy(mltrng + namelen, "multirange", NAMEDATALEN - namelen);
+ namelen += 10;
+ }
+
+ underscores = makeUniqueTypeName(mltrngunique, mltrng, namelen, typeNamespace, true);
+ if (underscores >= NAMEDATALEN - 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mltrngunique;
+}
+
+/*
+ * makeUniqueTypeName: Prepend underscores as needed until we make a name that
+ * doesn't collide with anything. Tries the original typeName if requested.
+ *
+ * Returns the number of underscores added.
+ */
+int
+makeUniqueTypeName(char *dest, const char *typeName, size_t namelen, Oid typeNamespace,
+ bool tryOriginalName)
+{
+ Relation pg_type_desc;
+ int i;
+
+ pg_type_desc = table_open(TypeRelationId, AccessShareLock);
+
+ for (i = 0; i < NAMEDATALEN - 1; i++)
+ {
+ if (i == 0)
+ {
+ if (tryOriginalName &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ {
+ strcpy(dest, typeName);
+ break;
+ }
+
+ }
+ else
+ {
+ dest[i - 1] = '_';
+ if (i + namelen < NAMEDATALEN)
+ strcpy(dest + i, typeName);
+ else
+ {
+ strlcpy(dest + i, typeName, NAMEDATALEN);
+ truncate_identifier(dest, NAMEDATALEN, false);
+ }
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ break;
+ }
+ }
+
+ table_close(pg_type_desc, AccessShareLock);
+
+ return i;
+}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 89887b8fd7..14a6857062 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -88,6 +88,8 @@ Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeArrayOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -811,7 +813,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1353,8 +1356,12 @@ DefineRange(CreateRangeStmt *stmt)
char *typeName;
Oid typeNamespace;
Oid typoid;
+ Oid mltrngtypoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1371,6 +1378,7 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1522,6 +1530,9 @@ DefineRange(CreateRangeStmt *stmt)
/* Allocate OID for array type */
rangeArrayOid = AssignTypeArrayOid();
+ /* Allocate OID for multirange array type */
+ multirangeArrayOid = AssignTypeArrayOid();
+
/* Create the pg_type entry */
address =
TypeCreate(InvalidOid, /* no predetermined type OID */
@@ -1557,9 +1568,47 @@ DefineRange(CreateRangeStmt *stmt)
InvalidOid); /* type's collation (ranges never have one) */
Assert(typoid == address.objectId);
+ /* Create the multirange that goes with it */
+
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(InvalidOid, /* no predetermined type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_MULTIRANGE, /* type-category (multirange type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ mltrngtypoid = mltrngaddress.objectId;
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, mltrngtypoid);
/*
* Create the array type that goes with it.
@@ -1600,8 +1649,49 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ mltrngtypoid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ mltrngtypoid, rangeArrayOid);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1679,6 +1769,83 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeArrayOid)
+{
+ static const char *const prosrc[2] = {"multirange_constructor0",
+ "multirange_constructor1"};
+ static const int pronargs[2] = {0, 1};
+
+ Oid constructorArgTypes[0];
+ ObjectAddress myself,
+ referenced;
+ int i;
+
+ constructorArgTypes[0] = rangeArrayOid;
+
+ Datum allParamTypes[1] = {ObjectIdGetDatum(rangeArrayOid)};
+ ArrayType *allParameterTypes = construct_array(allParamTypes, 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ Datum constructorAllParamTypes[2] = {PointerGetDatum(NULL), PointerGetDatum(allParameterTypes)};
+
+ Datum paramModes[1] = {CharGetDatum(FUNC_PARAM_VARIADIC)};
+ ArrayType *parameterModes = construct_array(paramModes, 1, CHAROID,
+ 1, true, 'c');
+ Datum constructorParamModes[2] = {PointerGetDatum(NULL), PointerGetDatum(parameterModes)};
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ for (i = 0; i < lengthof(prosrc); i++)
+ {
+ oidvector *constructorArgTypesVector;
+
+ constructorArgTypesVector = buildoidvector(constructorArgTypes,
+ pronargs[i]);
+
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace, /* namespace */
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR, /* language validator */
+ prosrc[i], /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ constructorArgTypesVector, /* parameterTypes */
+ constructorAllParamTypes[i], /* allParameterTypes */
+ constructorParamModes[i], /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+
+ /*
+ * Make the constructors internally-dependent on the multirange type
+ * so that they go away silently when the type is dropped. Note that
+ * pg_dump depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ }
+}
/*
* Find suitable I/O functions for a type.
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index a6c51a95da..f0b1f6f831 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -187,18 +187,20 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
- targetTypeId == ANYRANGEOID)
+ targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call anyarray_in,
- * anyenum_in, or anyrange_in, which will produce an error. Also, if
- * what we have is a domain over array, enum, or range, we have to
- * relabel it to its base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * anyarray_in, anyenum_in, anyrange_in, or anymultirange_in, which
+ * will produce an error. Also, if what we have is a domain over
+ * array, enum, range, or multirange, we have to relabel it to its
+ * base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1482,6 +1484,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid array_typelem;
Oid range_typeid = InvalidOid;
Oid range_typelem;
+ Oid multirange_typeid = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
@@ -1528,6 +1532,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1580,6 +1593,37 @@ check_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+ }
+
if (have_anynonarray)
{
/* require the element type to not be an array or domain over array */
@@ -1681,13 +1725,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid array_typelem;
Oid range_typelem;
+ Oid multirange_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
+ bool have_anyrange = (rettype == ANYRANGEOID);
+ bool have_anymultirange = (rettype == ANYMULTIRANGEOID);
/*
* Loop through the arguments to see if we have any that are polymorphic.
@@ -1745,7 +1793,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else if (decl_type == ANYRANGEOID)
{
- have_generics = true;
+ have_generics = have_anyrange = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
@@ -1763,6 +1811,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ have_generics = have_anymultirange = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
}
/*
@@ -1813,9 +1881,12 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- if (range_typeid == ANYRANGEOID && !have_anyelement)
+ if (range_typeid == ANYRANGEOID && !have_anyelement && !have_anymultirange)
{
- /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
+ /*
+ * Special case for ANYRANGE input: okay iff no ANYELEMENT or
+ * ANYMULTIRANGE
+ */
range_typelem = ANYELEMENTOID;
}
else
@@ -1849,6 +1920,69 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Get the range type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ if (multirange_typeid == ANYMULTIRANGEOID && !have_anyrange && !have_anyelement)
+ {
+ /*
+ * Special case for ANYMULTIRANGE input: okay iff no ANYRANGE or
+ * ANYELEMENT
+ */
+ multirange_typelem = ANYRANGEOID;
+ }
+ else
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+ }
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ /*
+ * Infer the element type too if needed (if there is an ANYMULTIRANGE
+ * and ANYELEMENT, with no ANYRANGE).
+ */
+ if (!OidIsValid(elem_typeid))
+ {
+ elem_typeid = get_range_subtype(range_typeid);
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
+ }
+
if (!OidIsValid(elem_typeid))
{
if (allow_poly)
@@ -1856,6 +1990,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -1928,6 +2063,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -1959,6 +2105,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
@@ -2007,8 +2169,7 @@ resolve_generic_type(Oid declared_type,
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
- context_declared_type == ANYENUMOID ||
- context_declared_type == ANYRANGEOID)
+ context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
@@ -2020,11 +2181,37 @@ resolve_generic_type(Oid declared_type,
format_type_be(context_actual_type))));
return array_typeid;
}
+ else if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid range_typelem = get_range_subtype(context_base_type);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+ Oid range_typelem = get_range_subtype(multirange_typelem);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
- declared_type == ANYENUMOID ||
- declared_type == ANYRANGEOID)
+ declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
@@ -2052,6 +2239,19 @@ resolve_generic_type(Oid declared_type,
"anyrange", format_type_be(context_base_type))));
return range_typelem;
}
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ /* Use the element type corresponding to actual type */
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
@@ -2060,6 +2260,74 @@ resolve_generic_type(Oid declared_type,
return context_actual_type;
}
}
+ else if (declared_type == ANYRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
+ else
+ {
+ /* We can't infer a range from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
+ else if (declared_type == ANYMULTIRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typeid = get_range_multirange(context_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return multirange_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else
+ {
+ /* We can't infer a multirange from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
else
{
/* declared_type isn't polymorphic, so return it as-is */
@@ -2174,6 +2442,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..7118dd0034 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..5323a6a635
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,966 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/hashutils.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ Oid typiofunc; /* range type's I/O function */
+ Oid typioparam; /* range type's I/O parameter */
+ FmgrInfo proc; /* lookup result for typiofunc */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData * get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks either either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ Oid rngtypoid;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+ rngtypoid = rangetyp->type_id;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */ ;
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **) repalloc(ranges,
+ range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(
+ InputFunctionCall(&cache->proc, range_str_copy,
+ cache->typioparam, typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "Unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->proc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(
+ ReceiveFunctionCall(&cache->proc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range = RangeTypePGetDatum(ranges[i]);
+ range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
+ uint32 range_len = VARSIZE(range) - VARHDRSZ;
+ char *range_data = VARDATA(range);
+
+ pq_sendint32(buf, range_len);
+ pq_sendbytes(buf, range_data, range_len);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &cache->typiofunc);
+
+ if (!OidIsValid(cache->typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(cache->typiofunc, &cache->proc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ RangeType *range;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ bytelen += MAXALIGN(VARSIZE(range));
+ }
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ memcpy(ptr, range, VARSIZE(range));
+ ptr += MAXALIGN(VARSIZE(range));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of any ranges you like into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ * Returns the number of slots actually used,
+ * which may be less than input_range_count but never more.
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType * multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR, (errmsg("Can't construct multirange with a multi-dimensional array")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL element")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ ereport(ERROR, (errmsg("Zero-param multirange constructor shouldn't have arguments")));
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 5c886cfe96..94484b3373 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -185,6 +186,30 @@ anyrange_out(PG_FUNCTION_ARGS)
}
/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
+/*
* void_in - input routine for pseudo-type VOID.
*
* We allow this so that PL functions can return VOID without any special
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 461c428413..65f68614c1 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -1177,12 +1175,68 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
+ return cmp;
+}
+
+/* btree comparator */
+Datum
+range_cmp(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int cmp;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ cmp = range_cmp_internal(typcache, r1, r2);
+
PG_FREE_IF_COPY(r1, 0);
PG_FREE_IF_COPY(r2, 1);
PG_RETURN_INT32(cmp);
}
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ /* For b-tree use, empty ranges sort before all else */
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
/* inequality operators using the range_cmp function */
Datum
range_lt(PG_FUNCTION_ARGS)
@@ -1218,13 +1272,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1284,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1325,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1354,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1392,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1938,6 +2006,45 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
}
/*
+ * Compares two ranges so we can qsort them.
+ * This expects that you give qsort a RangeType **,
+ * so the RangeTypes can be in diverse locations,
+ * as long as you have a list of pointers to them all.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
+/*
* Build an empty range value of the type indicated by the typcache entry.
*/
RangeType *
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 27602fa49c..360a71427e 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2469,6 +2469,16 @@ type_is_range(Oid typid)
}
/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
+/*
* get_type_category_preferred
*
* Given the type OID, fetch its category and preferred-type status.
@@ -3150,6 +3160,58 @@ get_range_subtype(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->mltrngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/* ---------- PG_MULTIRANGE CACHE ---------- */
+
+/*
+ * get_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_multirange_subtype(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 16297a52a1..3f0e7f8576 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -508,6 +508,17 @@ static const struct cachedesc cacheinfo[] = {
},
4
},
+ {RangeRelationId, /* MULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_mltrngtypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{NamespaceRelationId, /* NAMESPACENAME */
NamespaceNameIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 11920db0d9..0630687b49 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -278,6 +278,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -294,6 +295,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -500,8 +504,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -538,7 +542,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -563,7 +567,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -588,7 +592,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -640,6 +644,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -684,6 +695,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -759,6 +777,16 @@ lookup_type_cache(Oid type_id, int flags)
}
/*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
+ /*
* If requested, get information about a domain type
*/
if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
@@ -871,6 +899,33 @@ load_rangetype_info(TypeCacheEntry *typentry)
/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Form_pg_range pg_range;
+ HeapTuple tup;
+ Oid rangetypeOid;
+
+ /* get information from pg_range */
+ tup = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(typentry->type_id));
+ /* should not fail, since we already checked typtype ... */
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+ pg_range = (Form_pg_range) GETSTRUCT(tup);
+
+ rangetypeOid = pg_range->rngtypid;
+
+ ReleaseSysCache(tup);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
+
+
+/*
* load_domaintype_info --- helper routine to set up domain constraint info
*
* Note: we assume we're called in a relatively short-lived context, so it's
@@ -1469,11 +1524,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1516,6 +1571,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 4688fbc50c..d65968d4eb 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -470,11 +470,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anynonarray = false;
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
Oid anycollation = InvalidOid;
int i;
@@ -500,12 +502,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
case ANYRANGEOID:
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_anymultirange_result = true;
+ break;
default:
break;
}
}
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/*
@@ -533,6 +538,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (!OidIsValid(anyrange_type))
anyrange_type = get_call_expr_argtype(call_expr, i);
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(anymultirange_type))
+ anymultirange_type = get_call_expr_argtype(call_expr, i);
+ break;
default:
break;
}
@@ -540,7 +549,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/* If nothing found, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -561,19 +570,122 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ Oid rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ anymultirange_type = mltrngtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
/* Enforce ANYNONARRAY if needed */
if (have_anynonarray && type_is_array(anyelement_type))
@@ -640,6 +752,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
default:
break;
}
@@ -664,9 +784,11 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
int inargno;
int i;
@@ -725,6 +847,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ have_anymultirange_result = true;
+ else
+ {
+ if (!OidIsValid(anymultirange_type))
+ {
+ anymultirange_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anymultirange_type))
+ return false;
+ }
+ argtypes[i] = anymultirange_type;
+ }
+ break;
default:
break;
}
@@ -734,12 +871,12 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
/* Done? */
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/* If no input polymorphics, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -760,19 +897,122 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ Oid rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ }
+ else
+ {
+ return false;
+ }
+ }
/* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */
@@ -792,6 +1032,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = anymultirange_type;
+ break;
default:
break;
}
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 23cf481e78..50b4c4428f 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -137,8 +137,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 1f6de76e9c..c265ee4c09 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201910251
+#define CATALOG_VERSION_NO 201911061
#endif
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ef4445b017..ca9890ab6c 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -330,6 +330,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_mltrngtypid_index, 8001, on pg_range using btree(mltrngtypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 232557ee81..999493f94d 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 5e705019b4..b7cb2cb000 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -225,6 +225,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
@@ -385,6 +387,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index 2d575102ef..2e59cf9159 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index fa7dc96ece..4d319bb5b3 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3289,5 +3289,38 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'contsel', oprjoin => 'contjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 41e40d657a..536c10cb0c 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 58ea5b982b..7ffa047969 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9724,6 +9724,83 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index dd9baa2678..9fd781e7b5 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ mltrngtypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', mltrngtypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', mltrngtypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', mltrngtypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ mltrngtypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ mltrngtypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index b01a074d09..9cfc56d4d2 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -45,6 +45,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* subtype difference as a float8, or 0 */
regproc rngsubdiff BKI_LOOKUP(pg_proc);
+
+ /* OID of the range's multirange type */
+ Oid mltrngtypid BKI_LOOKUP(pg_type);
} FormData_pg_range;
/* ----------------
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index be49e00114..c95a38849a 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -486,6 +486,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -594,5 +628,10 @@
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 2a584b4b13..6a25fcc9bf 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -259,6 +259,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -270,6 +271,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPCATEGORY_ENUM 'E'
#define TYPCATEGORY_GEOMETRIC 'G'
#define TYPCATEGORY_NETWORK 'I' /* think INET */
+#define TYPCATEGORY_MULTIRANGE 'M'
#define TYPCATEGORY_NUMERIC 'N'
#define TYPCATEGORY_PSEUDOTYPE 'P'
#define TYPCATEGORY_RANGE 'R'
@@ -285,7 +287,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#endif /* EXPOSE_TO_CLIENT_CODE */
@@ -344,4 +347,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index c8df5bff9f..ffdd90d69c 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -154,6 +154,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -179,6 +180,8 @@ extern void free_attstatsslot(AttStatsSlot *sslot);
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..ed2e19aafa
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,72 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ char vl_len_[4]; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType * range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType * make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType * make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 11ed19ccfd..521710d71f 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 918765cc99..32603eeb58 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -66,6 +66,7 @@ enum SysCacheIdentifier
INDEXRELID,
LANGNAME,
LANGOID,
+ MULTIRANGETYPE,
NAMESPACENAME,
NAMESPACEOID,
OPERNAMENSP,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index 04bf28180d..eda39881ef 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -99,6 +99,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
/*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype;
+
+ /*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
*/
@@ -141,6 +146,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 3fb8fc7bad..f575f882a2 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -511,6 +511,8 @@ do_compile(FunctionCallInfo fcinfo,
rettypeid = INT4ARRAYOID;
else if (rettypeid == ANYRANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2494,6 +2496,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYRANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..5749537635
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,292 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index c19740e5db..916e1016b5 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -206,6 +213,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -224,6 +233,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -329,13 +340,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
@@ -343,16 +355,19 @@ ORDER BY 2;
2502 | anyarray_recv
2312 | anyelement_in
3504 | anyenum_in
+ 8002 | anymultirange_in
2777 | anynonarray_in
3832 | anyrange_in
750 | array_in
2400 | array_recv
3506 | enum_in
3532 | enum_recv
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(13 rows)
+(16 rows)
-- Look for functions that accept cstring and are neither datatype input
-- functions nor encoding conversion functions. It's almost never a good
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index cd7fc03b04..8ef9595c37 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -193,18 +193,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -238,17 +239,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -316,18 +318,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -361,17 +364,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -629,3 +633,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.mltrngtypid
+FROM pg_range p1
+WHERE p1.mltrngtypid IS NULL OR p1.mltrngtypid = 0;
+ rngtypid | rngsubtype | mltrngtypid
+----------+------------+-------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index fc0f14122b..c8c1cd0671 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 68ac56acdb..35155436f0 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..8651ba3f3d
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,65 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 624bea46ce..23932cfef0 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -273,13 +284,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- Look for functions that accept cstring and are neither datatype input
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fed413bf98..b102c01bbc 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -151,7 +151,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -180,7 +180,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -232,7 +232,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -261,7 +261,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -465,3 +465,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.mltrngtypid
+FROM pg_range p1
+WHERE p1.mltrngtypid IS NULL OR p1.mltrngtypid = 0;
--
2.11.0
[application/octet-stream] v4-0003-multirange-pg_dump.patch (1.6K, ../../CA+renyWY-vfLVM8NkHj11xXyCNYJA7=1sd5_Bk+B+DL66y6-WQ@mail.gmail.com/3-v4-0003-multirange-pg_dump.patch)
download | inline diff:
From 3d4609039bdc1ee9e6b8b50250116bf9ade60fc1 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:37 -0700
Subject: [PATCH v4 3/4] multirange pg_dump
---
src/bin/pg_dump/pg_dump.c | 7 ++++++-
src/bin/pg_dump/pg_dump.h | 1 +
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index bf69adc2f4..db84e79e11 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1583,7 +1583,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4923,6 +4923,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 7b2c1524a5..e27a752b6d 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -175,6 +175,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
--
2.11.0
[application/octet-stream] v4-0004-multirange-docs.patch (25.4K, ../../CA+renyWY-vfLVM8NkHj11xXyCNYJA7=1sd5_Bk+B+DL66y6-WQ@mail.gmail.com/4-v4-0004-multirange-docs.patch)
download | inline diff:
From f17f0d2710b22a25315c66b7b10fb43e8b6730ff Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:47 -0700
Subject: [PATCH v4 4/4] multirange docs
---
doc/src/sgml/extend.sgml | 28 +++-
doc/src/sgml/func.sgml | 302 +++++++++++++++++++++++++++++++++++++++----
doc/src/sgml/rangetypes.sgml | 47 ++++++-
3 files changed, 342 insertions(+), 35 deletions(-)
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index a3046f22d0..e72720cd68 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -229,9 +229,9 @@
</indexterm>
<para>
- Five pseudo-types of special interest are <type>anyelement</type>,
+ Six pseudo-types of special interest are <type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
- and <type>anyrange</type>,
+ <type>anyrange</type>, and <type>anymultirange</type>.
which are collectively called <firstterm>polymorphic types</firstterm>.
Any function declared using these types is said to be
a <firstterm>polymorphic function</firstterm>. A polymorphic function can
@@ -250,15 +250,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type>, the actual range type in
- the <type>anyrange</type> positions must be a range whose subtype is
- the same type appearing in the <type>anyelement</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -268,6 +268,20 @@
</para>
<para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type>, the actual range type in
+ the <type>anyrange</type> positions must be a range whose subtype is
+ the same type appearing in the <type>anyelement</type> positions.
+ The type <type>anymultirange</type> accepts a multirange
+ whose contents match the other polymorphic types.
+ That is it must hold ranges matching <type>anyrange</type>
+ and base type elements matching <type>anyelement</type> (if either of
+ those are present). If <type>anyarray</type> is present its elements
+ must match the base type of the <type>anyrange</type> and/or the base
+ type of the ranges in an </type>anymultirange</type>.
+ </para>
+
+ <para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
types are allowed. For example, a function declared as
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 28eb322f3f..54b428eb39 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14566,7 +14566,9 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14576,7 +14578,7 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14584,136 +14586,247 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>f</literal></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)</literallayout></entry>
<entry><literal>[5,20)</literal></entry>
</row>
<row>
+ <entry> <literal>@+</literal> </entry>
+ <entry>safe union</entry>
+ <entry>
+ <literallayout class="monospaced">numrange(5,10) @+ numrange(15,20)
+numrange(5,10) @+ '{[15,20)}'::nummultirange
+'{[5,10)}'::nummultirange @+ numrange(15,20)
+'{[5,10)}'::nummultirange @+ '{[15,20)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literal>{[5,10), [15,20)}</literal></entry>
+ </row>
+
+ <row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)</literallayout></entry>
<entry><literal>[10,15)</literal></entry>
</row>
<row>
+ <entry> <literal>@*</literal> </entry>
+ <entry>safe intersection</entry>
+ <entry>
+ <literallayout class="monospaced">int8range(5,15) @* int8range(10,20)
+int8range(5,15) @* '{[10,20)}'::int8multirange
+'{[5,15)}'::int8multirange @* int8range(10,20)
+'{[5,15)}'::int8multirange @* '{[10,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literal>{[10,15)}</literal></entry>
+ </row>
+
+ <row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)</literallayout></entry>
<entry><literal>[5,10)</literal></entry>
</row>
+ <row>
+ <entry> <literal>@-</literal> </entry>
+ <entry>safe difference</entry>
+ <entry>
+ <literallayout class="monospaced">int8range(5,20) @- int8range(10,15)
+int8range(5,20) @- '{[10,15)}'::int8multirange
+'{[5,20)}'::int8multirange @- int8range(10,15)
+'{[5,20)}'::int8multirange @- '{[10,15)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literal>{[5,10), [15,20)}</literal></entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -14729,19 +14842,28 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. The safe versions of union, intersection, and difference
+ return multiranges, so they can handle gaps without failing.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14793,6 +14915,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14804,6 +14937,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>isempty</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14815,6 +14959,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14826,6 +14981,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14837,6 +15003,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14848,6 +15025,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14859,6 +15047,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>range_merge</function>(<type>anyrange</type>, <type>anyrange</type>)
</literal>
</entry>
@@ -14867,16 +15066,27 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -15198,6 +15408,44 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>string_agg</primary>
</indexterm>
<function>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index 3a034d9b06..3e37b352fc 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -235,10 +242,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -272,6 +299,19 @@ SELECT int8range(1, 14, '(]');
SELECT numrange(NULL, 2.2);
</programlisting>
</para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
+</programlisting>
+ </para>
</sect2>
<sect2 id="rangetypes-discrete">
@@ -345,6 +385,11 @@ SELECT '[1.234, 5.678]'::floatrange;
</para>
<para>
+ When you define your own range your automatically get a corresponding
+ multirange type.
+ </para>
+
+ <para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
ordering that determines which values fall into a given range.
--
2.11.0
[application/octet-stream] v4-0002-multirange-operators-and-functions.patch (165.8K, ../../CA+renyWY-vfLVM8NkHj11xXyCNYJA7=1sd5_Bk+B+DL66y6-WQ@mail.gmail.com/5-v4-0002-multirange-operators-and-functions.patch)
download | inline diff:
From b51668e851bcdf402c0ac1945d51a8489c580457 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:20 -0700
Subject: [PATCH v4 2/4] multirange operators and functions
---
src/backend/utils/adt/multirangetypes.c | 1422 ++++++++++++++-
src/backend/utils/adt/rangetypes.c | 238 ++-
src/include/catalog/pg_aggregate.dat | 11 +
src/include/catalog/pg_amproc.dat | 5 +-
src/include/catalog/pg_operator.dat | 169 ++
src/include/catalog/pg_proc.dat | 185 ++
src/include/utils/multirangetypes.h | 34 +
src/include/utils/rangetypes.h | 31 +-
src/test/regress/expected/dependency.out | 1 +
src/test/regress/expected/multirangetypes.out | 2397 +++++++++++++++++++++++++
src/test/regress/expected/opr_sanity.out | 4 +-
src/test/regress/expected/rangetypes.out | 38 +-
src/test/regress/expected/sanity_check.out | 2 +
src/test/regress/sql/multirangetypes.sql | 607 +++++++
src/test/regress/sql/rangetypes.sql | 13 +
15 files changed, 5048 insertions(+), 109 deletions(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 5323a6a635..c509994796 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -726,45 +726,1267 @@ multirange_constructor0(PG_FUNCTION_ARGS)
}
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+range_union_range(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ MultirangeType *mr = make_multirange(mltrngtypoid, typcache->rngtype, 1, &r2);
+
+ PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r1, mr));
+}
+
+Datum
+range_union_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_union_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_union_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
+}
+
+MultirangeType *
+range_union_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (RangeIsEmpty(r))
+ return mr;
+ if (MultirangeIsEmpty(mr))
+ return make_multirange(typcache->type_id, typcache->rngtype, 1, &r);
+
+ multirange_deserialize(mr, &range_count, &ranges1);
+
+ ranges2 = palloc0((range_count + 1) * sizeof(RangeType *));
+
+ memcpy(ranges2, ranges1, range_count * sizeof(RangeType *));
+ ranges2[range_count] = r;
+ return make_multirange(typcache->type_id, typcache->rngtype, range_count + 1, ranges2);
+}
+
+/* multirange minus */
+Datum
+range_minus_range(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r1) || RangeIsEmpty(r2))
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, rangetyp, 1, &r1));
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r1,
+ 1,
+ &r2));
+}
+
+Datum
+range_minus_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, rangetyp, 1, &r));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r,
+ range_count,
+ ranges));
+}
+
+Datum
+multirange_minus_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr) || RangeIsEmpty(r))
+ PG_RETURN_MULTIRANGE_P(mr);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count,
+ ranges,
+ 1,
+ &r));
+}
+
+Datum
+multirange_minus_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1, int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+range_intersect_range(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r1) || RangeIsEmpty(r2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r1,
+ 1,
+ &r2));
+}
+
+Datum
+range_intersect_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr2);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count2;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r1,
+ range_count2,
+ ranges2));
+}
+
+Datum
+multirange_intersect_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ RangeType **ranges1;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || RangeIsEmpty(r2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ 1,
+ &r2));
+}
+
+Datum
+multirange_intersect_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1, int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1, but one extra won't
+ * hurt.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_multirange_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
/* multirange, multirange -> bool functions */
-/* equality (internal version) */
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType * mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges1[0], ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
bool
-multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1, MultirangeType * mr2)
{
- int32 range_count_1;
- int32 range_count_2;
- int32 i;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
RangeType **ranges1;
RangeType **ranges2;
RangeType *r1;
RangeType *r2;
+ int i1,
+ i2;
- /* Different types should be prevented by ANYMULTIRANGE matching rules */
- if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
- elog(ERROR, "multirange types do not match");
+ rangetyp = typcache->rngtype;
- multirange_deserialize(mr1, &range_count_1, &ranges1);
- multirange_deserialize(mr2, &range_count_2, &ranges2);
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
- if (range_count_1 != range_count_2)
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
return false;
- for (i = 0; i < range_count_1; i++)
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
{
- r1 = ranges1[i];
- r2 = ranges2[i];
+ r2 = ranges2[i2];
- if (!range_eq_internal(typcache->rngtype, r1, r2))
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
return false;
}
+ /* All ranges in mr2 are satisfied */
return true;
}
-/* equality */
+/* strictly left of? */
Datum
-multirange_eq(PG_FUNCTION_ARGS)
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
{
MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
@@ -772,27 +1994,156 @@ multirange_eq(PG_FUNCTION_ARGS)
typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
- PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
}
-/* inequality (internal version) */
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
bool
-multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
{
- return (!multirange_eq_internal(typcache, mr1, mr2));
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ int32 range_count;
+ RangeType **ranges;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
}
-/* inequality */
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ int32 range_count;
+ RangeType **ranges;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
Datum
-multirange_ne(PG_FUNCTION_ARGS)
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
{
MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
- PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
}
/* Btree support */
@@ -891,6 +2242,29 @@ multirange_gt(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(cmp > 0);
}
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
/* Hash support */
/* hash a multirange value */
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 65f68614c1..ece2215704 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -429,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -450,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -473,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -483,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -493,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -503,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -513,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -955,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -967,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -991,35 +1034,35 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
-range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
+RangeType *
+range_union_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
bool strict)
{
RangeBound lower1,
@@ -1099,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1108,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1130,54 +1180,83 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
}
-/* Btree support */
+/* range, range -> range, range functions */
-/* btree comparator */
-Datum
-range_cmp(PG_FUNCTION_ARGS)
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
{
- RangeType *r1 = PG_GETARG_RANGE_P(0);
- RangeType *r2 = PG_GETARG_RANGE_P(1);
- TypeCacheEntry *typcache;
RangeBound lower1,
lower2;
RangeBound upper1,
upper2;
bool empty1,
empty2;
- int cmp;
-
- check_stack_depth(); /* recurses when subtype is a range type */
-
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- /* For b-tree use, empty ranges sort before all else */
- if (empty1 && empty2)
- cmp = 0;
- else if (empty1)
- cmp = -1;
- else if (empty2)
- cmp = 1;
- else
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
{
- cmp = range_cmp_bounds(typcache, &lower1, &lower2);
- if (cmp == 0)
- cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
}
- return cmp;
+ return false;
+}
+
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
}
+
+/* Btree support */
+
/* btree comparator */
Datum
range_cmp(PG_FUNCTION_ARGS)
@@ -1207,7 +1286,7 @@ range_cmp(PG_FUNCTION_ARGS)
* Internal version of range_cmp
*/
int
-range_cmp_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
{
RangeBound lower1,
lower2;
@@ -1273,7 +1352,7 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
uint32
-hash_range_internal(TypeCacheEntry *typcache, RangeType *r)
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
uint32 result;
TypeCacheEntry *scache;
@@ -1343,7 +1422,7 @@ hash_range(PG_FUNCTION_ARGS)
}
uint64
-hash_range_extended_internal(TypeCacheEntry *typcache, RangeType *r, Datum seed)
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
{
uint64 result;
TypeCacheEntry *scache;
@@ -1838,6 +1917,21 @@ range_get_flags(const RangeType *range)
}
/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
+/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
* This is only needed in GiST operations, so we don't include a provision
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index 242d843347..07a7211c15 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index b7cb2cb000..32965d6b3d 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -1210,12 +1210,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 4d319bb5b3..ade223b6f3 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3322,5 +3322,174 @@
oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8138', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anyrange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@+(anyrange,anyrange)',
+ oprcode => 'range_union_range' },
+{ oid => '8115', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@+(anymultirange,anyrange)',
+ oprcode => 'range_union_multirange' },
+{ oid => '8116', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@+(anyrange,anymultirange)',
+ oprcode => 'multirange_union_range' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union_multirange' },
+{ oid => '8140', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anyrange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcode => 'range_minus_range' },
+{ oid => '8121', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'range_minus_multirange' },
+{ oid => '8122', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus_range' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus_multirange' },
+{ oid => '8142', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anyrange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@*(anyrange,anyrange)',
+ oprcode => 'range_intersect_range' },
+{ oid => '8127', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@*(anymultirange,anyrange)',
+ oprcode => 'range_intersect_multirange' },
+{ oid => '8128', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@*(anyrange,anymultirange)',
+ oprcode => 'multirange_intersect_range' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect_multirange' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 7ffa047969..13ea94c024 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9607,6 +9607,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9656,6 +9660,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9743,9 +9754,165 @@
{ oid => '8007', descr => 'I/O',
proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
{ oid => '8008', descr => 'multirange typanalyze',
proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8137',
+ proname => 'range_union_range', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_union_range' },
+{ oid => '8112',
+ proname => 'range_union_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_union_multirange' },
+{ oid => '8113',
+ proname => 'multirange_union_range', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_union_range' },
+{ oid => '8114',
+ proname => 'multirange_union_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union_multirange' },
+{ oid => '8139',
+ proname => 'range_minus_range', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_minus_range' },
+{ oid => '8118',
+ proname => 'range_minus_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_minus_multirange' },
+{ oid => '8119',
+ proname => 'multirange_minus_range', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_minus_range' },
+{ oid => '8120',
+ proname => 'multirange_minus_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus_multirange' },
+{ oid => '8141',
+ proname => 'range_intersect_range', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_range' },
+{ oid => '8124',
+ proname => 'range_intersect_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_intersect_multirange' },
+{ oid => '8125',
+ proname => 'multirange_intersect_range', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_intersect_range' },
+{ oid => '8126',
+ proname => 'multirange_intersect_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_multirange' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
{ oid => '8045', descr => 'int4multirange constructor',
proname => 'int4multirange', proisstrict => 'f',
@@ -9801,6 +9968,24 @@
prorettype => 'int8multirange', proargtypes => '_int8range',
proallargtypes => '{_int8range}', proargmodes => '{v}',
prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
index ed2e19aafa..cc4f82b0ba 100644
--- a/src/include/utils/multirangetypes.h
+++ b/src/include/utils/multirangetypes.h
@@ -59,6 +59,40 @@ extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr
MultirangeType * mr2);
extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
MultirangeType * mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern MultirangeType * range_union_multirange_internal(TypeCacheEntry *typcache,
+ RangeType *r,
+ MultirangeType * mr);
+extern MultirangeType * multirange_minus_multirange_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType * multirange_intersect_multirange_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
/* assorted support functions */
extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 521710d71f..b801f20fe1 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -94,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -117,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -127,15 +141,20 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
- const RangeBound *b2);
+ const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
- const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+ const RangeBound *b2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..778699a961 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -141,6 +141,7 @@ owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
owner of type deptest_range
+owner of type deptest_multirange
owner of table deptest2
owner of sequence ss1
owner of type deptest_t
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
index 5749537635..2276d6d002 100644
--- a/src/test/regress/expected/multirangetypes.out
+++ b/src/test/regress/expected/multirangetypes.out
@@ -290,3 +290,2400 @@ select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
{[a,d)}
(1 row)
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT 'empty'::numrange @+ 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @+ nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @+ 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @+ nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @+ numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT 'empty'::numrange @+ nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @+ numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @+ nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(2,4);
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(3,4);
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT 'empty'::numrange @- 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @- nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT 'empty'::numrange @- numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @- 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @- numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(1,3);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(1,2);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(2,4);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(3,4);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(1,3));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,10) @- nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------------
+ {[2,4),[5,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(0,2);
+ ?column?
+----------
+ {[2,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(1,2);
+ ?column?
+----------
+ {[2,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(3,4);
+ ?column?
+----------------
+ {[1,3),[4,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(13,18);
+ ?column?
+----------
+ {[1,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) @- nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) @- nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT 'empty'::numrange @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,3);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,5);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(2,5);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,5) @* numrange(2,3);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,3);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,5);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(2,5);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @* numrange(2,3);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,2) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,5) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(2,5) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(2,3) @* '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange @* '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange @* '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select $1 @+ $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 916e1016b5..e818eaea79 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1101,13 +1101,15 @@ ORDER BY 1, 2;
?| | ?|
?|| | ?||
@ | ~
+ @* | @*
+ @+ | @+
@@ | @@
@@@ | @@@
| | |
~<=~ | ~>=~
~<~ | ~>~
~= | ~=
-(30 rows)
+(32 rows)
-- Likewise for negator pairs.
SELECT DISTINCT o1.oprname AS op1, o2.oprname AS op2
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 6fd16bddd1..1bfe426be6 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1284,7 +1302,7 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
@@ -1392,6 +1410,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1400,6 +1419,16 @@ select * from outparam_succeed(int4range(1,2));
[1,2) | foo
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1408,6 +1437,7 @@ select * from inoutparam_succeed(int4range(1,2));
2 | [1,2)
(1 row)
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
select * from table_succeed(123, int4range(1,11));
@@ -1420,14 +1450,14 @@ select * from table_succeed(123, int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 070de78e85..6f7fcf6326 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
onek|t
onek2|t
path_tbl|f
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
index 8651ba3f3d..b3ede18f96 100644
--- a/src/test/regress/sql/multirangetypes.sql
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -63,3 +63,610 @@ select textmultirange();
select textmultirange(textrange('a', 'c'));
select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT 'empty'::numrange @+ 'empty'::numrange;
+SELECT 'empty'::numrange @+ nummultirange();
+SELECT nummultirange() @+ 'empty'::numrange;
+SELECT nummultirange() @+ nummultirange();
+SELECT 'empty'::numrange @+ numrange(1,2);
+SELECT 'empty'::numrange @+ nummultirange(numrange(1,2));
+SELECT numrange(1,2) @+ nummultirange();
+SELECT nummultirange(numrange(1,2)) @+ 'empty'::numrange;
+SELECT numrange(1,2) @+ 'empty'::numrange;
+SELECT nummultirange() @+ numrange(1,2);
+SELECT nummultirange() @+ nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange();
+SELECT numrange(1,2) @+ numrange(1,2);
+SELECT numrange(1,2) @+ numrange(2,4);
+SELECT numrange(1,2) @+ numrange(3,4);
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT 'empty'::numrange @- 'empty'::numrange;
+SELECT 'empty'::numrange @- nummultirange();
+SELECT nummultirange() @- 'empty'::numrange;
+SELECT nummultirange() @- nummultirange();
+SELECT 'empty'::numrange @- numrange(1,2);
+SELECT 'empty'::numrange @- nummultirange(numrange(1,2));
+SELECT numrange(1,2) @- nummultirange();
+SELECT nummultirange(numrange(1,2)) @- 'empty'::numrange;
+SELECT 'empty'::numrange @- numrange(1,2);
+SELECT numrange(1,2) @- 'empty'::numrange;
+SELECT nummultirange() @- numrange(1,2);
+SELECT nummultirange() @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @- nummultirange();
+SELECT numrange(1,3) @- numrange(1,3);
+SELECT numrange(1,3) @- numrange(1,2);
+SELECT numrange(1,3) @- numrange(2,4);
+SELECT numrange(1,3) @- numrange(3,4);
+SELECT numrange(1,3) @- nummultirange(numrange(1,3));
+SELECT numrange(1,3) @- nummultirange(numrange(1,2));
+SELECT numrange(1,3) @- nummultirange(numrange(2,4));
+SELECT numrange(1,3) @- nummultirange(numrange(3,4));
+SELECT numrange(1,10) @- nummultirange(numrange(1,2), numrange(4,5));
+SELECT nummultirange(numrange(1,10)) @- numrange(0,2);
+SELECT nummultirange(numrange(1,10)) @- numrange(1,2);
+SELECT nummultirange(numrange(1,10)) @- numrange(3,4);
+SELECT nummultirange(numrange(1,10)) @- numrange(13,18);
+SELECT nummultirange(numrange(1,2), numrange(3,4)) @- nummultirange();
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) @- nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT 'empty'::numrange @* 'empty'::numrange;
+SELECT 'empty'::numrange @* nummultirange();
+SELECT nummultirange() @* 'empty'::numrange;
+SELECT nummultirange() @* nummultirange();
+SELECT 'empty'::numrange @* numrange(1,2);
+SELECT 'empty'::numrange @* nummultirange(numrange(1,2));
+SELECT numrange(1,2) @* 'empty'::numrange;
+SELECT numrange(1,2) @* nummultirange();
+SELECT nummultirange(numrange(1,2)) @* 'empty'::numrange;
+SELECT nummultirange() @* numrange(1,2);
+SELECT nummultirange() @* nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @* nummultirange();
+SELECT numrange(1,3) @* numrange(1,3);
+SELECT numrange(1,3) @* numrange(1,2);
+SELECT numrange(1,3) @* numrange(1,5);
+SELECT numrange(1,3) @* numrange(2,5);
+SELECT numrange(1,5) @* numrange(2,3);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,3);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,2);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,5);
+SELECT '{[1,3)}'::nummultirange @* numrange(2,5);
+SELECT '{[1,5)}'::nummultirange @* numrange(2,3);
+SELECT numrange(1,3) @* '{[1,3)}'::nummultirange;
+SELECT numrange(1,2) @* '{[1,3)}'::nummultirange;
+SELECT numrange(1,5) @* '{[1,3)}'::nummultirange;
+SELECT numrange(2,5) @* '{[1,3)}'::nummultirange;
+SELECT numrange(2,3) @* '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange @* '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange @* '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select $1 @+ $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 8960add976..32e18661d0 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -481,16 +485,25 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
--
2.11.0
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-11-07 02:35 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2019-11-07 02:35 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Wed, Nov 6, 2019 at 3:02 PM Paul A Jungwirth
<pj@illuminatedcomputing.com> wrote:
> On Thu, Sep 26, 2019 at 2:13 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
> > Hello Paul, I've started to review this patch. Here's a few minor
> > things I ran across -- mostly compiler warnings (is my compiler too
> > ancient?).
> I just opened this thread to post a rebased set patches (especially
> because of the `const` additions to range functions). Maybe it's not
> that helpful since they don't include your changes yet but here they
> are anyway. I'll post some more with your changes shortly.
Here is another batch of patches incorporating your improvements. It
seems like almost all the warnings were about moving variable
declarations above any other statements. For some reason I don't get
warnings about that on my end (compiling on OS X):
platter:postgres paul$ gcc --version
Configured with:
--prefix=/Applications/Xcode.app/Contents/Developer/usr
--with-gxx-include-dir=/usr/include/c++/4.2.1
Apple clang version 11.0.0 (clang-1100.0.33.12)
Target: x86_64-apple-darwin18.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
For configure I'm saying this:
./configure CFLAGS=-ggdb 5-Og -g3 -fno-omit-frame-pointer
--enable-tap-tests --enable-cassert --enable-debug
--prefix=/Users/paul/local
Any suggestions to get better warnings? On my other patch I got
feedback about the very same kind. I could just compile on Linux but
it's nice to work on this away from my desk on the laptop. Maybe
installing a real gcc is the way to go.
Thanks,
Paul
Attachments:
[application/octet-stream] v5-0004-multirange-docs.patch (25.4K, ../../CA+renyW9KU=1mxyoNMuHPnBx4j5eFw+KKPgEMLcbKRUVekrPoA@mail.gmail.com/2-v5-0004-multirange-docs.patch)
download | inline diff:
From 0a662c1d27358477e69615bdd6d14df9ba2ce297 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:47 -0700
Subject: [PATCH v5 4/4] multirange docs
---
doc/src/sgml/extend.sgml | 28 +++-
doc/src/sgml/func.sgml | 302 +++++++++++++++++++++++++++++++++++++++----
doc/src/sgml/rangetypes.sgml | 47 ++++++-
3 files changed, 342 insertions(+), 35 deletions(-)
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index a3046f22d0..e72720cd68 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -229,9 +229,9 @@
</indexterm>
<para>
- Five pseudo-types of special interest are <type>anyelement</type>,
+ Six pseudo-types of special interest are <type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
- and <type>anyrange</type>,
+ <type>anyrange</type>, and <type>anymultirange</type>.
which are collectively called <firstterm>polymorphic types</firstterm>.
Any function declared using these types is said to be
a <firstterm>polymorphic function</firstterm>. A polymorphic function can
@@ -250,15 +250,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type>, the actual range type in
- the <type>anyrange</type> positions must be a range whose subtype is
- the same type appearing in the <type>anyelement</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -268,6 +268,20 @@
</para>
<para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type>, the actual range type in
+ the <type>anyrange</type> positions must be a range whose subtype is
+ the same type appearing in the <type>anyelement</type> positions.
+ The type <type>anymultirange</type> accepts a multirange
+ whose contents match the other polymorphic types.
+ That is it must hold ranges matching <type>anyrange</type>
+ and base type elements matching <type>anyelement</type> (if either of
+ those are present). If <type>anyarray</type> is present its elements
+ must match the base type of the <type>anyrange</type> and/or the base
+ type of the ranges in an </type>anymultirange</type>.
+ </para>
+
+ <para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
types are allowed. For example, a function declared as
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 28eb322f3f..54b428eb39 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14566,7 +14566,9 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14576,7 +14578,7 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14584,136 +14586,247 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>f</literal></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)</literallayout></entry>
<entry><literal>[5,20)</literal></entry>
</row>
<row>
+ <entry> <literal>@+</literal> </entry>
+ <entry>safe union</entry>
+ <entry>
+ <literallayout class="monospaced">numrange(5,10) @+ numrange(15,20)
+numrange(5,10) @+ '{[15,20)}'::nummultirange
+'{[5,10)}'::nummultirange @+ numrange(15,20)
+'{[5,10)}'::nummultirange @+ '{[15,20)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literal>{[5,10), [15,20)}</literal></entry>
+ </row>
+
+ <row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)</literallayout></entry>
<entry><literal>[10,15)</literal></entry>
</row>
<row>
+ <entry> <literal>@*</literal> </entry>
+ <entry>safe intersection</entry>
+ <entry>
+ <literallayout class="monospaced">int8range(5,15) @* int8range(10,20)
+int8range(5,15) @* '{[10,20)}'::int8multirange
+'{[5,15)}'::int8multirange @* int8range(10,20)
+'{[5,15)}'::int8multirange @* '{[10,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literal>{[10,15)}</literal></entry>
+ </row>
+
+ <row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)</literallayout></entry>
<entry><literal>[5,10)</literal></entry>
</row>
+ <row>
+ <entry> <literal>@-</literal> </entry>
+ <entry>safe difference</entry>
+ <entry>
+ <literallayout class="monospaced">int8range(5,20) @- int8range(10,15)
+int8range(5,20) @- '{[10,15)}'::int8multirange
+'{[5,20)}'::int8multirange @- int8range(10,15)
+'{[5,20)}'::int8multirange @- '{[10,15)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literal>{[5,10), [15,20)}</literal></entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -14729,19 +14842,28 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. The safe versions of union, intersection, and difference
+ return multiranges, so they can handle gaps without failing.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14793,6 +14915,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14804,6 +14937,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>isempty</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14815,6 +14959,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14826,6 +14981,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14837,6 +15003,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14848,6 +15025,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14859,6 +15047,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>range_merge</function>(<type>anyrange</type>, <type>anyrange</type>)
</literal>
</entry>
@@ -14867,16 +15066,27 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -15198,6 +15408,44 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>string_agg</primary>
</indexterm>
<function>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index 3a034d9b06..3e37b352fc 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -235,10 +242,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -272,6 +299,19 @@ SELECT int8range(1, 14, '(]');
SELECT numrange(NULL, 2.2);
</programlisting>
</para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
+</programlisting>
+ </para>
</sect2>
<sect2 id="rangetypes-discrete">
@@ -345,6 +385,11 @@ SELECT '[1.234, 5.678]'::floatrange;
</para>
<para>
+ When you define your own range your automatically get a corresponding
+ multirange type.
+ </para>
+
+ <para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
ordering that determines which values fall into a given range.
--
2.11.0
[application/octet-stream] v5-0002-multirange-operators-and-functions.patch (179.7K, ../../CA+renyW9KU=1mxyoNMuHPnBx4j5eFw+KKPgEMLcbKRUVekrPoA@mail.gmail.com/3-v5-0002-multirange-operators-and-functions.patch)
download | inline diff:
From a54a44248f043f60f4aee5fc7b5f8d5e1cbd5100 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:20 -0700
Subject: [PATCH v5 2/4] multirange operators and functions
---
src/backend/catalog/pg_type.c | 2 -
src/backend/commands/typecmds.c | 6 +-
src/backend/parser/parse_coerce.c | 2 +
src/backend/utils/adt/multirangetypes.c | 1575 +++++++++++++++-
src/backend/utils/adt/rangetypes.c | 236 ++-
src/backend/utils/fmgr/funcapi.c | 97 +-
src/include/catalog/pg_aggregate.dat | 11 +
src/include/catalog/pg_amproc.dat | 5 +-
src/include/catalog/pg_operator.dat | 169 ++
src/include/catalog/pg_proc.dat | 185 ++
src/include/utils/multirangetypes.h | 34 +
src/include/utils/rangetypes.h | 31 +-
src/test/regress/expected/dependency.out | 1 +
src/test/regress/expected/multirangetypes.out | 2397 +++++++++++++++++++++++++
src/test/regress/expected/opr_sanity.out | 4 +-
src/test/regress/expected/rangetypes.out | 38 +-
src/test/regress/expected/sanity_check.out | 2 +
src/test/regress/sql/multirangetypes.sql | 607 +++++++
src/test/regress/sql/rangetypes.sql | 13 +
19 files changed, 5188 insertions(+), 227 deletions(-)
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 08552850c8..ea5b161c4d 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -876,7 +876,6 @@ makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
char mltrng[NAMEDATALEN];
char *mltrngunique = (char *) palloc(NAMEDATALEN);
int namelen = strlen(rangeTypeName);
- int rangelen;
char *rangestr;
int rangeoffset;
int underscores;
@@ -892,7 +891,6 @@ makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
if (rangestr)
{
rangeoffset = rangestr - rangeTypeName;
- rangelen = strlen(rangestr);
strlcpy(mltrng + rangeoffset, "multi", NAMEDATALEN - rangeoffset);
strlcpy(mltrng + rangeoffset + 5, rangestr, NAMEDATALEN - rangeoffset - 5);
namelen += 5;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 14a6857062..7a440cafd1 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1783,13 +1783,11 @@ makeMultirangeConstructors(const char *name, Oid namespace,
"multirange_constructor1"};
static const int pronargs[2] = {0, 1};
- Oid constructorArgTypes[0];
+ Oid constructorArgTypes = rangeArrayOid;
ObjectAddress myself,
referenced;
int i;
- constructorArgTypes[0] = rangeArrayOid;
-
Datum allParamTypes[1] = {ObjectIdGetDatum(rangeArrayOid)};
ArrayType *allParameterTypes = construct_array(allParamTypes, 1, OIDOID,
sizeof(Oid), true, 'i');
@@ -1808,7 +1806,7 @@ makeMultirangeConstructors(const char *name, Oid namespace,
{
oidvector *constructorArgTypesVector;
- constructorArgTypesVector = buildoidvector(constructorArgTypes,
+ constructorArgTypesVector = buildoidvector(&constructorArgTypes,
pronargs[i]);
myself = ProcedureCreate(name, /* name: same as multirange type */
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index f0b1f6f831..2ff039ab6c 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1918,6 +1918,8 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(range_typeid),
format_type_be(elem_typeid))));
}
+ else
+ range_typelem = InvalidOid;
}
/* Get the range type based on the multirange type, if we have one */
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 5323a6a635..7717acb3b1 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -91,7 +91,6 @@ multirange_in(PG_FUNCTION_ARGS)
Oid mltrngtypoid = PG_GETARG_OID(1);
Oid typmod = PG_GETARG_INT32(2);
TypeCacheEntry *rangetyp;
- Oid rngtypoid;
int32 ranges_seen = 0;
int32 range_count = 0;
int32 range_capacity = 8;
@@ -101,13 +100,12 @@ multirange_in(PG_FUNCTION_ARGS)
MultirangeType *ret;
MultirangeParseState parse_state;
const char *ptr = input_str;
- const char *range_str;
+ const char *range_str = NULL;
int32 range_str_len;
char *range_str_copy;
cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
rangetyp = cache->typcache->rngtype;
- rngtypoid = rangetyp->type_id;
/* consume whitespace */
while (*ptr != '\0' && isspace((unsigned char) *ptr))
@@ -351,9 +349,12 @@ multirange_send(PG_FUNCTION_ARGS)
for (i = 0; i < range_count; i++)
{
Datum range = RangeTypePGetDatum(ranges[i]);
+ uint32 range_len;
+ char *range_data;
+
range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
- uint32 range_len = VARSIZE(range) - VARHDRSZ;
- char *range_data = VARDATA(range);
+ range_len = VARSIZE(range) - VARHDRSZ;
+ range_data = VARDATA(range);
pq_sendint32(buf, range_len);
pq_sendbytes(buf, range_data, range_len);
@@ -726,169 +727,1547 @@ multirange_constructor0(PG_FUNCTION_ARGS)
}
-/* multirange, multirange -> bool functions */
+/* multirange, multirange -> multirange type functions */
-/* equality (internal version) */
-bool
-multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+/* multirange union */
+Datum
+range_union_range(PG_FUNCTION_ARGS)
{
- int32 range_count_1;
- int32 range_count_2;
- int32 i;
- RangeType **ranges1;
- RangeType **ranges2;
- RangeType *r1;
- RangeType *r2;
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ MultirangeType *mr = make_multirange(mltrngtypoid, typcache->rngtype, 1, &r2);
- /* Different types should be prevented by ANYMULTIRANGE matching rules */
- if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
- elog(ERROR, "multirange types do not match");
+ PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r1, mr));
+}
- multirange_deserialize(mr1, &range_count_1, &ranges1);
- multirange_deserialize(mr2, &range_count_2, &ranges2);
+Datum
+range_union_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
- if (range_count_1 != range_count_2)
- return false;
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
- for (i = 0; i < range_count_1; i++)
- {
- r1 = ranges1[i];
- r2 = ranges2[i];
+ PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r, mr));
+}
- if (!range_eq_internal(typcache->rngtype, r1, r2))
- return false;
- }
+Datum
+multirange_union_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
- return true;
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r, mr));
}
-/* equality */
Datum
-multirange_eq(PG_FUNCTION_ARGS)
+multirange_union_multirange(PG_FUNCTION_ARGS)
{
MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
- PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
}
-/* inequality (internal version) */
-bool
-multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+MultirangeType *
+range_union_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
{
- return (!multirange_eq_internal(typcache, mr1, mr2));
+ int32 range_count;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (RangeIsEmpty(r))
+ return mr;
+ if (MultirangeIsEmpty(mr))
+ return make_multirange(typcache->type_id, typcache->rngtype, 1, &r);
+
+ multirange_deserialize(mr, &range_count, &ranges1);
+
+ ranges2 = palloc0((range_count + 1) * sizeof(RangeType *));
+
+ memcpy(ranges2, ranges1, range_count * sizeof(RangeType *));
+ ranges2[range_count] = r;
+ return make_multirange(typcache->type_id, typcache->rngtype, range_count + 1, ranges2);
}
-/* inequality */
+/* multirange minus */
Datum
-multirange_ne(PG_FUNCTION_ARGS)
+range_minus_range(PG_FUNCTION_ARGS)
{
- MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
- MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
- typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
- PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+ if (RangeIsEmpty(r1) || RangeIsEmpty(r2))
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, rangetyp, 1, &r1));
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r1,
+ 1,
+ &r2));
}
-/* Btree support */
+Datum
+range_minus_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, rangetyp, 1, &r));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r,
+ range_count,
+ ranges));
+}
-/* btree comparator */
Datum
-multirange_cmp(PG_FUNCTION_ARGS)
+multirange_minus_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr) || RangeIsEmpty(r))
+ PG_RETURN_MULTIRANGE_P(mr);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count,
+ ranges,
+ 1,
+ &r));
+}
+
+Datum
+multirange_minus_multirange(PG_FUNCTION_ARGS)
{
MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
- int32 range_count_1;
- int32 range_count_2;
- int32 range_count_max;
- int32 i;
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
RangeType **ranges1;
RangeType **ranges2;
- RangeType *r1;
- RangeType *r2;
- TypeCacheEntry *typcache;
- int cmp = 0; /* If both are empty we'll use this. */
- /* Different types should be prevented by ANYMULTIRANGE matching rules */
- if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
- elog(ERROR, "multirange types do not match");
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
- typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
- multirange_deserialize(mr1, &range_count_1, &ranges1);
- multirange_deserialize(mr2, &range_count_2, &ranges2);
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
- /* Loop over source data */
- range_count_max = Max(range_count_1, range_count_2);
- for (i = 0; i < range_count_max; i++)
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1, int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
{
- /*
- * If one multirange is shorter, it's as if it had empty ranges at the
- * end to extend its length. An empty range compares earlier than any
- * other range, so the shorter multirange comes before the longer.
- * This is the same behavior as in other types, e.g. in strings 'aaa'
- * < 'aaaaaa'.
- */
- if (i >= range_count_1)
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
{
- cmp = -1;
- break;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
}
- if (i >= range_count_2)
+
+ while (r2 != NULL)
{
- cmp = 1;
- break;
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
}
- r1 = ranges1[i];
- r2 = ranges2[i];
- cmp = range_cmp_internal(typcache->rngtype, r1, r2);
- if (cmp != 0)
- break;
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
}
- PG_FREE_IF_COPY(mr1, 0);
- PG_FREE_IF_COPY(mr2, 1);
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
- PG_RETURN_INT32(cmp);
+/* multirange intersection */
+Datum
+range_intersect_range(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r1) || RangeIsEmpty(r2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r1,
+ 1,
+ &r2));
}
-/* inequality operators using the multirange_cmp function */
Datum
-multirange_lt(PG_FUNCTION_ARGS)
+range_intersect_multirange(PG_FUNCTION_ARGS)
{
- int cmp = multirange_cmp(fcinfo);
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr2);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count2;
+ RangeType **ranges2;
- PG_RETURN_BOOL(cmp < 0);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r1,
+ range_count2,
+ ranges2));
}
Datum
-multirange_le(PG_FUNCTION_ARGS)
+multirange_intersect_range(PG_FUNCTION_ARGS)
{
- int cmp = multirange_cmp(fcinfo);
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ RangeType **ranges1;
- PG_RETURN_BOOL(cmp <= 0);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || RangeIsEmpty(r2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ 1,
+ &r2));
}
Datum
-multirange_ge(PG_FUNCTION_ARGS)
+multirange_intersect_multirange(PG_FUNCTION_ARGS)
{
- int cmp = multirange_cmp(fcinfo);
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
- PG_RETURN_BOOL(cmp >= 0);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+ /*
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1, but one extra won't
+ * hurt.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
}
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
Datum
-multirange_gt(PG_FUNCTION_ARGS)
+range_agg_transfn(PG_FUNCTION_ARGS)
{
- int cmp = multirange_cmp(fcinfo);
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
- PG_RETURN_BOOL(cmp > 0);
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_multirange_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType * mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges1[0], ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1, MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
+ return false;
+
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
+ return false;
+ }
+
+ /* All ranges in mr2 are satisfied */
+ return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
}
/* Hash support */
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 65f68614c1..0651618363 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -429,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -450,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -473,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -483,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -493,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -503,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -513,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -955,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -967,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -991,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1099,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1108,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1130,54 +1180,83 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
}
-/* Btree support */
+/* range, range -> range, range functions */
-/* btree comparator */
-Datum
-range_cmp(PG_FUNCTION_ARGS)
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
{
- RangeType *r1 = PG_GETARG_RANGE_P(0);
- RangeType *r2 = PG_GETARG_RANGE_P(1);
- TypeCacheEntry *typcache;
RangeBound lower1,
lower2;
RangeBound upper1,
upper2;
bool empty1,
empty2;
- int cmp;
-
- check_stack_depth(); /* recurses when subtype is a range type */
-
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- /* For b-tree use, empty ranges sort before all else */
- if (empty1 && empty2)
- cmp = 0;
- else if (empty1)
- cmp = -1;
- else if (empty2)
- cmp = 1;
- else
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
{
- cmp = range_cmp_bounds(typcache, &lower1, &lower2);
- if (cmp == 0)
- cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
}
- return cmp;
+ return false;
+}
+
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
}
+
+/* Btree support */
+
/* btree comparator */
Datum
range_cmp(PG_FUNCTION_ARGS)
@@ -1207,7 +1286,7 @@ range_cmp(PG_FUNCTION_ARGS)
* Internal version of range_cmp
*/
int
-range_cmp_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
{
RangeBound lower1,
lower2;
@@ -1273,7 +1352,7 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
uint32
-hash_range_internal(TypeCacheEntry *typcache, RangeType *r)
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
uint32 result;
TypeCacheEntry *scache;
@@ -1343,7 +1422,7 @@ hash_range(PG_FUNCTION_ARGS)
}
uint64
-hash_range_extended_internal(TypeCacheEntry *typcache, RangeType *r, Datum seed)
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
{
uint64 result;
TypeCacheEntry *scache;
@@ -1838,6 +1917,21 @@ range_get_flags(const RangeType *range)
}
/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
+/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
* This is only needed in GiST operations, so we don't include a provision
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index d65968d4eb..25623461bb 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -573,18 +573,20 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
anyrange_type = rngtype;
-
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -628,18 +630,21 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
{
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
anyrange_type = rngtype;
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -660,21 +665,25 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
{
if (OidIsValid(anyrange_type))
{
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and range results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
return false;
anyelement_type = subtype;
- Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
- anyrange_type,
- ANYRANGEOID);
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent range and multirange results */
- Oid rngtype = get_multirange_subtype(mltrngtype);
+ rngtype = get_multirange_subtype(mltrngtype);
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
@@ -900,18 +909,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
anyrange_type = rngtype;
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -955,18 +967,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
{
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
anyrange_type = rngtype;
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -987,21 +1002,25 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
{
if (OidIsValid(anyrange_type))
{
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and range results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
return false;
anyelement_type = subtype;
- Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
- anyrange_type,
- ANYRANGEOID);
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent range and multirange results */
- Oid rngtype = get_multirange_subtype(mltrngtype);
+ rngtype = get_multirange_subtype(mltrngtype);
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index 242d843347..07a7211c15 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index b7cb2cb000..32965d6b3d 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -1210,12 +1210,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 4d319bb5b3..ade223b6f3 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3322,5 +3322,174 @@
oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8138', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anyrange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@+(anyrange,anyrange)',
+ oprcode => 'range_union_range' },
+{ oid => '8115', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@+(anymultirange,anyrange)',
+ oprcode => 'range_union_multirange' },
+{ oid => '8116', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@+(anyrange,anymultirange)',
+ oprcode => 'multirange_union_range' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union_multirange' },
+{ oid => '8140', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anyrange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcode => 'range_minus_range' },
+{ oid => '8121', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'range_minus_multirange' },
+{ oid => '8122', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus_range' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus_multirange' },
+{ oid => '8142', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anyrange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@*(anyrange,anyrange)',
+ oprcode => 'range_intersect_range' },
+{ oid => '8127', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@*(anymultirange,anyrange)',
+ oprcode => 'range_intersect_multirange' },
+{ oid => '8128', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@*(anyrange,anymultirange)',
+ oprcode => 'multirange_intersect_range' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect_multirange' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 7ffa047969..13ea94c024 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9607,6 +9607,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9656,6 +9660,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9743,9 +9754,165 @@
{ oid => '8007', descr => 'I/O',
proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
{ oid => '8008', descr => 'multirange typanalyze',
proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8137',
+ proname => 'range_union_range', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_union_range' },
+{ oid => '8112',
+ proname => 'range_union_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_union_multirange' },
+{ oid => '8113',
+ proname => 'multirange_union_range', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_union_range' },
+{ oid => '8114',
+ proname => 'multirange_union_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union_multirange' },
+{ oid => '8139',
+ proname => 'range_minus_range', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_minus_range' },
+{ oid => '8118',
+ proname => 'range_minus_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_minus_multirange' },
+{ oid => '8119',
+ proname => 'multirange_minus_range', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_minus_range' },
+{ oid => '8120',
+ proname => 'multirange_minus_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus_multirange' },
+{ oid => '8141',
+ proname => 'range_intersect_range', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_range' },
+{ oid => '8124',
+ proname => 'range_intersect_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_intersect_multirange' },
+{ oid => '8125',
+ proname => 'multirange_intersect_range', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_intersect_range' },
+{ oid => '8126',
+ proname => 'multirange_intersect_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_multirange' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
{ oid => '8045', descr => 'int4multirange constructor',
proname => 'int4multirange', proisstrict => 'f',
@@ -9801,6 +9968,24 @@
prorettype => 'int8multirange', proargtypes => '_int8range',
proallargtypes => '{_int8range}', proargmodes => '{v}',
prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
index ed2e19aafa..cc4f82b0ba 100644
--- a/src/include/utils/multirangetypes.h
+++ b/src/include/utils/multirangetypes.h
@@ -59,6 +59,40 @@ extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr
MultirangeType * mr2);
extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
MultirangeType * mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern MultirangeType * range_union_multirange_internal(TypeCacheEntry *typcache,
+ RangeType *r,
+ MultirangeType * mr);
+extern MultirangeType * multirange_minus_multirange_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType * multirange_intersect_multirange_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
/* assorted support functions */
extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 521710d71f..0bee724886 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -94,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -117,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -127,15 +141,20 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
- const RangeBound *b2);
+ const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
- const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+ const RangeBound *b2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..778699a961 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -141,6 +141,7 @@ owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
owner of type deptest_range
+owner of type deptest_multirange
owner of table deptest2
owner of sequence ss1
owner of type deptest_t
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
index 5749537635..2276d6d002 100644
--- a/src/test/regress/expected/multirangetypes.out
+++ b/src/test/regress/expected/multirangetypes.out
@@ -290,3 +290,2400 @@ select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
{[a,d)}
(1 row)
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT 'empty'::numrange @+ 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @+ nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @+ 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @+ nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @+ numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT 'empty'::numrange @+ nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @+ numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @+ nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(2,4);
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(3,4);
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT 'empty'::numrange @- 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @- nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT 'empty'::numrange @- numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @- 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @- numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(1,3);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(1,2);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(2,4);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(3,4);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(1,3));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,10) @- nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------------
+ {[2,4),[5,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(0,2);
+ ?column?
+----------
+ {[2,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(1,2);
+ ?column?
+----------
+ {[2,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(3,4);
+ ?column?
+----------------
+ {[1,3),[4,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(13,18);
+ ?column?
+----------
+ {[1,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) @- nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) @- nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT 'empty'::numrange @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,3);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,5);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(2,5);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,5) @* numrange(2,3);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,3);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,5);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(2,5);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @* numrange(2,3);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,2) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,5) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(2,5) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(2,3) @* '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange @* '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange @* '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select $1 @+ $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 916e1016b5..e818eaea79 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1101,13 +1101,15 @@ ORDER BY 1, 2;
?| | ?|
?|| | ?||
@ | ~
+ @* | @*
+ @+ | @+
@@ | @@
@@@ | @@@
| | |
~<=~ | ~>=~
~<~ | ~>~
~= | ~=
-(30 rows)
+(32 rows)
-- Likewise for negator pairs.
SELECT DISTINCT o1.oprname AS op1, o2.oprname AS op2
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 6fd16bddd1..1bfe426be6 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1284,7 +1302,7 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
@@ -1392,6 +1410,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1400,6 +1419,16 @@ select * from outparam_succeed(int4range(1,2));
[1,2) | foo
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1408,6 +1437,7 @@ select * from inoutparam_succeed(int4range(1,2));
2 | [1,2)
(1 row)
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
select * from table_succeed(123, int4range(1,11));
@@ -1420,14 +1450,14 @@ select * from table_succeed(123, int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 070de78e85..6f7fcf6326 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
onek|t
onek2|t
path_tbl|f
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
index 8651ba3f3d..b3ede18f96 100644
--- a/src/test/regress/sql/multirangetypes.sql
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -63,3 +63,610 @@ select textmultirange();
select textmultirange(textrange('a', 'c'));
select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT 'empty'::numrange @+ 'empty'::numrange;
+SELECT 'empty'::numrange @+ nummultirange();
+SELECT nummultirange() @+ 'empty'::numrange;
+SELECT nummultirange() @+ nummultirange();
+SELECT 'empty'::numrange @+ numrange(1,2);
+SELECT 'empty'::numrange @+ nummultirange(numrange(1,2));
+SELECT numrange(1,2) @+ nummultirange();
+SELECT nummultirange(numrange(1,2)) @+ 'empty'::numrange;
+SELECT numrange(1,2) @+ 'empty'::numrange;
+SELECT nummultirange() @+ numrange(1,2);
+SELECT nummultirange() @+ nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange();
+SELECT numrange(1,2) @+ numrange(1,2);
+SELECT numrange(1,2) @+ numrange(2,4);
+SELECT numrange(1,2) @+ numrange(3,4);
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT 'empty'::numrange @- 'empty'::numrange;
+SELECT 'empty'::numrange @- nummultirange();
+SELECT nummultirange() @- 'empty'::numrange;
+SELECT nummultirange() @- nummultirange();
+SELECT 'empty'::numrange @- numrange(1,2);
+SELECT 'empty'::numrange @- nummultirange(numrange(1,2));
+SELECT numrange(1,2) @- nummultirange();
+SELECT nummultirange(numrange(1,2)) @- 'empty'::numrange;
+SELECT 'empty'::numrange @- numrange(1,2);
+SELECT numrange(1,2) @- 'empty'::numrange;
+SELECT nummultirange() @- numrange(1,2);
+SELECT nummultirange() @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @- nummultirange();
+SELECT numrange(1,3) @- numrange(1,3);
+SELECT numrange(1,3) @- numrange(1,2);
+SELECT numrange(1,3) @- numrange(2,4);
+SELECT numrange(1,3) @- numrange(3,4);
+SELECT numrange(1,3) @- nummultirange(numrange(1,3));
+SELECT numrange(1,3) @- nummultirange(numrange(1,2));
+SELECT numrange(1,3) @- nummultirange(numrange(2,4));
+SELECT numrange(1,3) @- nummultirange(numrange(3,4));
+SELECT numrange(1,10) @- nummultirange(numrange(1,2), numrange(4,5));
+SELECT nummultirange(numrange(1,10)) @- numrange(0,2);
+SELECT nummultirange(numrange(1,10)) @- numrange(1,2);
+SELECT nummultirange(numrange(1,10)) @- numrange(3,4);
+SELECT nummultirange(numrange(1,10)) @- numrange(13,18);
+SELECT nummultirange(numrange(1,2), numrange(3,4)) @- nummultirange();
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) @- nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT 'empty'::numrange @* 'empty'::numrange;
+SELECT 'empty'::numrange @* nummultirange();
+SELECT nummultirange() @* 'empty'::numrange;
+SELECT nummultirange() @* nummultirange();
+SELECT 'empty'::numrange @* numrange(1,2);
+SELECT 'empty'::numrange @* nummultirange(numrange(1,2));
+SELECT numrange(1,2) @* 'empty'::numrange;
+SELECT numrange(1,2) @* nummultirange();
+SELECT nummultirange(numrange(1,2)) @* 'empty'::numrange;
+SELECT nummultirange() @* numrange(1,2);
+SELECT nummultirange() @* nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @* nummultirange();
+SELECT numrange(1,3) @* numrange(1,3);
+SELECT numrange(1,3) @* numrange(1,2);
+SELECT numrange(1,3) @* numrange(1,5);
+SELECT numrange(1,3) @* numrange(2,5);
+SELECT numrange(1,5) @* numrange(2,3);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,3);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,2);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,5);
+SELECT '{[1,3)}'::nummultirange @* numrange(2,5);
+SELECT '{[1,5)}'::nummultirange @* numrange(2,3);
+SELECT numrange(1,3) @* '{[1,3)}'::nummultirange;
+SELECT numrange(1,2) @* '{[1,3)}'::nummultirange;
+SELECT numrange(1,5) @* '{[1,3)}'::nummultirange;
+SELECT numrange(2,5) @* '{[1,3)}'::nummultirange;
+SELECT numrange(2,3) @* '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange @* '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange @* '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select $1 @+ $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 8960add976..32e18661d0 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -481,16 +485,25 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
--
2.11.0
[application/octet-stream] v5-0001-multirange-type-basics.patch (140.5K, ../../CA+renyW9KU=1mxyoNMuHPnBx4j5eFw+KKPgEMLcbKRUVekrPoA@mail.gmail.com/4-v5-0001-multirange-type-basics.patch)
download | inline diff:
From fcf6446aff14bda7128995ee72eb8942420cefc6 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:25:39 -0700
Subject: [PATCH v5 1/4] multirange type basics
---
src/backend/catalog/pg_proc.c | 16 +-
src/backend/catalog/pg_range.c | 11 +-
src/backend/catalog/pg_type.c | 134 +++-
src/backend/commands/typecmds.c | 171 ++++-
src/backend/parser/parse_coerce.c | 301 +++++++-
src/backend/utils/adt/Makefile | 1 +
src/backend/utils/adt/multirangetypes.c | 966 ++++++++++++++++++++++++++
src/backend/utils/adt/pseudotypes.c | 25 +
src/backend/utils/adt/rangetypes.c | 155 ++++-
src/backend/utils/cache/lsyscache.c | 62 ++
src/backend/utils/cache/syscache.c | 11 +
src/backend/utils/cache/typcache.c | 109 ++-
src/backend/utils/fmgr/funcapi.c | 271 +++++++-
src/include/access/tupmacs.h | 4 +-
src/include/catalog/catversion.h | 2 +-
src/include/catalog/indexing.h | 3 +
src/include/catalog/pg_amop.dat | 22 +
src/include/catalog/pg_amproc.dat | 7 +
src/include/catalog/pg_opclass.dat | 4 +
src/include/catalog/pg_operator.dat | 33 +
src/include/catalog/pg_opfamily.dat | 4 +
src/include/catalog/pg_proc.dat | 77 ++
src/include/catalog/pg_range.dat | 15 +-
src/include/catalog/pg_range.h | 5 +-
src/include/catalog/pg_type.dat | 39 ++
src/include/catalog/pg_type.h | 8 +-
src/include/utils/lsyscache.h | 3 +
src/include/utils/multirangetypes.h | 72 ++
src/include/utils/rangetypes.h | 2 +
src/include/utils/syscache.h | 1 +
src/include/utils/typcache.h | 6 +
src/pl/plpgsql/src/pl_comp.c | 5 +
src/test/regress/expected/hash_func.out | 13 +
src/test/regress/expected/multirangetypes.out | 292 ++++++++
src/test/regress/expected/opr_sanity.out | 23 +-
src/test/regress/expected/type_sanity.out | 46 +-
src/test/regress/parallel_schedule | 3 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/hash_func.sql | 10 +
src/test/regress/sql/multirangetypes.sql | 65 ++
src/test/regress/sql/opr_sanity.sql | 18 +-
src/test/regress/sql/type_sanity.sql | 16 +-
42 files changed, 2895 insertions(+), 137 deletions(-)
create mode 100644 src/backend/utils/adt/multirangetypes.c
create mode 100644 src/include/utils/multirangetypes.h
create mode 100644 src/test/regress/expected/multirangetypes.out
create mode 100644 src/test/regress/sql/multirangetypes.sql
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index ef009ad2bc..80cc0721a5 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -192,6 +192,7 @@ ProcedureCreate(const char *procedureName,
genericInParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericInParam = true;
anyrangeInParam = true;
break;
@@ -219,6 +220,7 @@ ProcedureCreate(const char *procedureName,
genericOutParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericOutParam = true;
anyrangeOutParam = true;
break;
@@ -231,10 +233,10 @@ ProcedureCreate(const char *procedureName,
/*
* Do not allow polymorphic return type unless at least one input argument
- * is polymorphic. ANYRANGE return type is even stricter: must have an
- * ANYRANGE input (since we can't deduce the specific range type from
- * ANYELEMENT). Also, do not allow return type INTERNAL unless at least
- * one input argument is INTERNAL.
+ * is polymorphic. ANYRANGE and ANYMULTIRANGE return types are even
+ * stricter: must have an ANYRANGE or ANYMULTIRANGE input (since we can't
+ * deduce the specific range type from ANYELEMENT). Also, do not allow
+ * return type INTERNAL unless at least one input argument is INTERNAL.
*/
if ((IsPolymorphicType(returnType) || genericOutParam)
&& !genericInParam)
@@ -243,12 +245,12 @@ ProcedureCreate(const char *procedureName,
errmsg("cannot determine result data type"),
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
- if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
- !anyrangeInParam)
+ if ((returnType == ANYRANGEOID || returnType == ANYMULTIRANGEOID
+ || anyrangeOutParam) && !anyrangeInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
- errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
+ errdetail("A function returning \"anyrange\" or \"anymultirange\" must have at least one \"anyrange\" or \"anymultirange\" argument.")));
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
ereport(ERROR,
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index e6e138babd..d914f04858 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_mltrngtypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,13 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 2a51501d8d..08552850c8 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -36,6 +36,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static int makeUniqueTypeName(char *dest, const char *typeName, size_t namelen,
+ Oid typeNamespace, bool tryOriginalName);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -785,34 +788,10 @@ makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
char *arr = (char *) palloc(NAMEDATALEN);
int namelen = strlen(typeName);
- Relation pg_type_desc;
- int i;
-
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- pg_type_desc = table_open(TypeRelationId, AccessShareLock);
-
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
-
- table_close(pg_type_desc, AccessShareLock);
+ int underscores;
- if (i >= NAMEDATALEN - 1)
+ underscores = makeUniqueTypeName(arr, typeName, namelen, typeNamespace, false);
+ if (underscores >= NAMEDATALEN - 1)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -883,3 +862,104 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * the caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char mltrng[NAMEDATALEN];
+ char *mltrngunique = (char *) palloc(NAMEDATALEN);
+ int namelen = strlen(rangeTypeName);
+ int rangelen;
+ char *rangestr;
+ int rangeoffset;
+ int underscores;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ strlcpy(mltrng, rangeTypeName, NAMEDATALEN);
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ rangeoffset = rangestr - rangeTypeName;
+ rangelen = strlen(rangestr);
+ strlcpy(mltrng + rangeoffset, "multi", NAMEDATALEN - rangeoffset);
+ strlcpy(mltrng + rangeoffset + 5, rangestr, NAMEDATALEN - rangeoffset - 5);
+ namelen += 5;
+ }
+ else
+ {
+ strlcpy(mltrng + namelen, "multirange", NAMEDATALEN - namelen);
+ namelen += 10;
+ }
+
+ underscores = makeUniqueTypeName(mltrngunique, mltrng, namelen, typeNamespace, true);
+ if (underscores >= NAMEDATALEN - 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mltrngunique;
+}
+
+/*
+ * makeUniqueTypeName: Prepend underscores as needed until we make a name that
+ * doesn't collide with anything. Tries the original typeName if requested.
+ *
+ * Returns the number of underscores added.
+ */
+int
+makeUniqueTypeName(char *dest, const char *typeName, size_t namelen, Oid typeNamespace,
+ bool tryOriginalName)
+{
+ Relation pg_type_desc;
+ int i;
+
+ pg_type_desc = table_open(TypeRelationId, AccessShareLock);
+
+ for (i = 0; i < NAMEDATALEN - 1; i++)
+ {
+ if (i == 0)
+ {
+ if (tryOriginalName &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ {
+ strcpy(dest, typeName);
+ break;
+ }
+
+ }
+ else
+ {
+ dest[i - 1] = '_';
+ if (i + namelen < NAMEDATALEN)
+ strcpy(dest + i, typeName);
+ else
+ {
+ strlcpy(dest + i, typeName, NAMEDATALEN);
+ truncate_identifier(dest, NAMEDATALEN, false);
+ }
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ break;
+ }
+ }
+
+ table_close(pg_type_desc, AccessShareLock);
+
+ return i;
+}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 89887b8fd7..14a6857062 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -88,6 +88,8 @@ Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeArrayOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -811,7 +813,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1353,8 +1356,12 @@ DefineRange(CreateRangeStmt *stmt)
char *typeName;
Oid typeNamespace;
Oid typoid;
+ Oid mltrngtypoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1371,6 +1378,7 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1522,6 +1530,9 @@ DefineRange(CreateRangeStmt *stmt)
/* Allocate OID for array type */
rangeArrayOid = AssignTypeArrayOid();
+ /* Allocate OID for multirange array type */
+ multirangeArrayOid = AssignTypeArrayOid();
+
/* Create the pg_type entry */
address =
TypeCreate(InvalidOid, /* no predetermined type OID */
@@ -1557,9 +1568,47 @@ DefineRange(CreateRangeStmt *stmt)
InvalidOid); /* type's collation (ranges never have one) */
Assert(typoid == address.objectId);
+ /* Create the multirange that goes with it */
+
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(InvalidOid, /* no predetermined type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_MULTIRANGE, /* type-category (multirange type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ mltrngtypoid = mltrngaddress.objectId;
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, mltrngtypoid);
/*
* Create the array type that goes with it.
@@ -1600,8 +1649,49 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ mltrngtypoid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ mltrngtypoid, rangeArrayOid);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1679,6 +1769,83 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeArrayOid)
+{
+ static const char *const prosrc[2] = {"multirange_constructor0",
+ "multirange_constructor1"};
+ static const int pronargs[2] = {0, 1};
+
+ Oid constructorArgTypes[0];
+ ObjectAddress myself,
+ referenced;
+ int i;
+
+ constructorArgTypes[0] = rangeArrayOid;
+
+ Datum allParamTypes[1] = {ObjectIdGetDatum(rangeArrayOid)};
+ ArrayType *allParameterTypes = construct_array(allParamTypes, 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ Datum constructorAllParamTypes[2] = {PointerGetDatum(NULL), PointerGetDatum(allParameterTypes)};
+
+ Datum paramModes[1] = {CharGetDatum(FUNC_PARAM_VARIADIC)};
+ ArrayType *parameterModes = construct_array(paramModes, 1, CHAROID,
+ 1, true, 'c');
+ Datum constructorParamModes[2] = {PointerGetDatum(NULL), PointerGetDatum(parameterModes)};
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ for (i = 0; i < lengthof(prosrc); i++)
+ {
+ oidvector *constructorArgTypesVector;
+
+ constructorArgTypesVector = buildoidvector(constructorArgTypes,
+ pronargs[i]);
+
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace, /* namespace */
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR, /* language validator */
+ prosrc[i], /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ constructorArgTypesVector, /* parameterTypes */
+ constructorAllParamTypes[i], /* allParameterTypes */
+ constructorParamModes[i], /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+
+ /*
+ * Make the constructors internally-dependent on the multirange type
+ * so that they go away silently when the type is dropped. Note that
+ * pg_dump depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ }
+}
/*
* Find suitable I/O functions for a type.
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index a6c51a95da..f0b1f6f831 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -187,18 +187,20 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
- targetTypeId == ANYRANGEOID)
+ targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call anyarray_in,
- * anyenum_in, or anyrange_in, which will produce an error. Also, if
- * what we have is a domain over array, enum, or range, we have to
- * relabel it to its base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * anyarray_in, anyenum_in, anyrange_in, or anymultirange_in, which
+ * will produce an error. Also, if what we have is a domain over
+ * array, enum, range, or multirange, we have to relabel it to its
+ * base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1482,6 +1484,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid array_typelem;
Oid range_typeid = InvalidOid;
Oid range_typelem;
+ Oid multirange_typeid = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
@@ -1528,6 +1532,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1580,6 +1593,37 @@ check_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+ }
+
if (have_anynonarray)
{
/* require the element type to not be an array or domain over array */
@@ -1681,13 +1725,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid array_typelem;
Oid range_typelem;
+ Oid multirange_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
+ bool have_anyrange = (rettype == ANYRANGEOID);
+ bool have_anymultirange = (rettype == ANYMULTIRANGEOID);
/*
* Loop through the arguments to see if we have any that are polymorphic.
@@ -1745,7 +1793,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else if (decl_type == ANYRANGEOID)
{
- have_generics = true;
+ have_generics = have_anyrange = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
@@ -1763,6 +1811,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ have_generics = have_anymultirange = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
}
/*
@@ -1813,9 +1881,12 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- if (range_typeid == ANYRANGEOID && !have_anyelement)
+ if (range_typeid == ANYRANGEOID && !have_anyelement && !have_anymultirange)
{
- /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
+ /*
+ * Special case for ANYRANGE input: okay iff no ANYELEMENT or
+ * ANYMULTIRANGE
+ */
range_typelem = ANYELEMENTOID;
}
else
@@ -1849,6 +1920,69 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Get the range type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ if (multirange_typeid == ANYMULTIRANGEOID && !have_anyrange && !have_anyelement)
+ {
+ /*
+ * Special case for ANYMULTIRANGE input: okay iff no ANYRANGE or
+ * ANYELEMENT
+ */
+ multirange_typelem = ANYRANGEOID;
+ }
+ else
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+ }
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ /*
+ * Infer the element type too if needed (if there is an ANYMULTIRANGE
+ * and ANYELEMENT, with no ANYRANGE).
+ */
+ if (!OidIsValid(elem_typeid))
+ {
+ elem_typeid = get_range_subtype(range_typeid);
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
+ }
+
if (!OidIsValid(elem_typeid))
{
if (allow_poly)
@@ -1856,6 +1990,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -1928,6 +2063,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -1959,6 +2105,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
@@ -2007,8 +2169,7 @@ resolve_generic_type(Oid declared_type,
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
- context_declared_type == ANYENUMOID ||
- context_declared_type == ANYRANGEOID)
+ context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
@@ -2020,11 +2181,37 @@ resolve_generic_type(Oid declared_type,
format_type_be(context_actual_type))));
return array_typeid;
}
+ else if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid range_typelem = get_range_subtype(context_base_type);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+ Oid range_typelem = get_range_subtype(multirange_typelem);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
- declared_type == ANYENUMOID ||
- declared_type == ANYRANGEOID)
+ declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
@@ -2052,6 +2239,19 @@ resolve_generic_type(Oid declared_type,
"anyrange", format_type_be(context_base_type))));
return range_typelem;
}
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ /* Use the element type corresponding to actual type */
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
@@ -2060,6 +2260,74 @@ resolve_generic_type(Oid declared_type,
return context_actual_type;
}
}
+ else if (declared_type == ANYRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
+ else
+ {
+ /* We can't infer a range from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
+ else if (declared_type == ANYMULTIRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typeid = get_range_multirange(context_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return multirange_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else
+ {
+ /* We can't infer a multirange from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
else
{
/* declared_type isn't polymorphic, so return it as-is */
@@ -2174,6 +2442,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..7118dd0034 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..5323a6a635
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,966 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/hashutils.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ Oid typiofunc; /* range type's I/O function */
+ Oid typioparam; /* range type's I/O parameter */
+ FmgrInfo proc; /* lookup result for typiofunc */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData * get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks either either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ Oid rngtypoid;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+ rngtypoid = rangetyp->type_id;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */ ;
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **) repalloc(ranges,
+ range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(
+ InputFunctionCall(&cache->proc, range_str_copy,
+ cache->typioparam, typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "Unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->proc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(
+ ReceiveFunctionCall(&cache->proc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range = RangeTypePGetDatum(ranges[i]);
+ range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
+ uint32 range_len = VARSIZE(range) - VARHDRSZ;
+ char *range_data = VARDATA(range);
+
+ pq_sendint32(buf, range_len);
+ pq_sendbytes(buf, range_data, range_len);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &cache->typiofunc);
+
+ if (!OidIsValid(cache->typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(cache->typiofunc, &cache->proc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ RangeType *range;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ bytelen += MAXALIGN(VARSIZE(range));
+ }
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ memcpy(ptr, range, VARSIZE(range));
+ ptr += MAXALIGN(VARSIZE(range));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of any ranges you like into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ * Returns the number of slots actually used,
+ * which may be less than input_range_count but never more.
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType * multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR, (errmsg("Can't construct multirange with a multi-dimensional array")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL element")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ ereport(ERROR, (errmsg("Zero-param multirange constructor shouldn't have arguments")));
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 5c886cfe96..94484b3373 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -185,6 +186,30 @@ anyrange_out(PG_FUNCTION_ARGS)
}
/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
+/*
* void_in - input routine for pseudo-type VOID.
*
* We allow this so that PL functions can return VOID without any special
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 461c428413..65f68614c1 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -1177,12 +1175,68 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
+ return cmp;
+}
+
+/* btree comparator */
+Datum
+range_cmp(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int cmp;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ cmp = range_cmp_internal(typcache, r1, r2);
+
PG_FREE_IF_COPY(r1, 0);
PG_FREE_IF_COPY(r2, 1);
PG_RETURN_INT32(cmp);
}
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ /* For b-tree use, empty ranges sort before all else */
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
/* inequality operators using the range_cmp function */
Datum
range_lt(PG_FUNCTION_ARGS)
@@ -1218,13 +1272,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1284,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1325,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1354,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1392,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1938,6 +2006,45 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
}
/*
+ * Compares two ranges so we can qsort them.
+ * This expects that you give qsort a RangeType **,
+ * so the RangeTypes can be in diverse locations,
+ * as long as you have a list of pointers to them all.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
+/*
* Build an empty range value of the type indicated by the typcache entry.
*/
RangeType *
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 27602fa49c..360a71427e 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2469,6 +2469,16 @@ type_is_range(Oid typid)
}
/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
+/*
* get_type_category_preferred
*
* Given the type OID, fetch its category and preferred-type status.
@@ -3150,6 +3160,58 @@ get_range_subtype(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->mltrngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/* ---------- PG_MULTIRANGE CACHE ---------- */
+
+/*
+ * get_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_multirange_subtype(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 16297a52a1..3f0e7f8576 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -508,6 +508,17 @@ static const struct cachedesc cacheinfo[] = {
},
4
},
+ {RangeRelationId, /* MULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_mltrngtypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{NamespaceRelationId, /* NAMESPACENAME */
NamespaceNameIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 11920db0d9..0630687b49 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -278,6 +278,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -294,6 +295,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -500,8 +504,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -538,7 +542,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -563,7 +567,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -588,7 +592,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -640,6 +644,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -684,6 +695,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -759,6 +777,16 @@ lookup_type_cache(Oid type_id, int flags)
}
/*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
+ /*
* If requested, get information about a domain type
*/
if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
@@ -871,6 +899,33 @@ load_rangetype_info(TypeCacheEntry *typentry)
/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Form_pg_range pg_range;
+ HeapTuple tup;
+ Oid rangetypeOid;
+
+ /* get information from pg_range */
+ tup = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(typentry->type_id));
+ /* should not fail, since we already checked typtype ... */
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+ pg_range = (Form_pg_range) GETSTRUCT(tup);
+
+ rangetypeOid = pg_range->rngtypid;
+
+ ReleaseSysCache(tup);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
+
+
+/*
* load_domaintype_info --- helper routine to set up domain constraint info
*
* Note: we assume we're called in a relatively short-lived context, so it's
@@ -1469,11 +1524,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1516,6 +1571,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 4688fbc50c..d65968d4eb 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -470,11 +470,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anynonarray = false;
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
Oid anycollation = InvalidOid;
int i;
@@ -500,12 +502,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
case ANYRANGEOID:
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_anymultirange_result = true;
+ break;
default:
break;
}
}
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/*
@@ -533,6 +538,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (!OidIsValid(anyrange_type))
anyrange_type = get_call_expr_argtype(call_expr, i);
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(anymultirange_type))
+ anymultirange_type = get_call_expr_argtype(call_expr, i);
+ break;
default:
break;
}
@@ -540,7 +549,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/* If nothing found, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -561,19 +570,122 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ Oid rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ anymultirange_type = mltrngtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
/* Enforce ANYNONARRAY if needed */
if (have_anynonarray && type_is_array(anyelement_type))
@@ -640,6 +752,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
default:
break;
}
@@ -664,9 +784,11 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
int inargno;
int i;
@@ -725,6 +847,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ have_anymultirange_result = true;
+ else
+ {
+ if (!OidIsValid(anymultirange_type))
+ {
+ anymultirange_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anymultirange_type))
+ return false;
+ }
+ argtypes[i] = anymultirange_type;
+ }
+ break;
default:
break;
}
@@ -734,12 +871,12 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
/* Done? */
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/* If no input polymorphics, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -760,19 +897,122 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ Oid rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ }
+ else
+ {
+ return false;
+ }
+ }
/* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */
@@ -792,6 +1032,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = anymultirange_type;
+ break;
default:
break;
}
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 23cf481e78..50b4c4428f 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -137,8 +137,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 1f6de76e9c..c265ee4c09 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201910251
+#define CATALOG_VERSION_NO 201911061
#endif
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ef4445b017..ca9890ab6c 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -330,6 +330,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_mltrngtypid_index, 8001, on pg_range using btree(mltrngtypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 232557ee81..999493f94d 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 5e705019b4..b7cb2cb000 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -225,6 +225,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
@@ -385,6 +387,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index 2d575102ef..2e59cf9159 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index fa7dc96ece..4d319bb5b3 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3289,5 +3289,38 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'contsel', oprjoin => 'contjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 41e40d657a..536c10cb0c 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 58ea5b982b..7ffa047969 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9724,6 +9724,83 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index dd9baa2678..9fd781e7b5 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ mltrngtypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', mltrngtypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', mltrngtypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', mltrngtypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ mltrngtypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ mltrngtypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index b01a074d09..9cfc56d4d2 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -45,6 +45,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* subtype difference as a float8, or 0 */
regproc rngsubdiff BKI_LOOKUP(pg_proc);
+
+ /* OID of the range's multirange type */
+ Oid mltrngtypid BKI_LOOKUP(pg_type);
} FormData_pg_range;
/* ----------------
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index be49e00114..c95a38849a 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -486,6 +486,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -594,5 +628,10 @@
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 2a584b4b13..6a25fcc9bf 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -259,6 +259,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -270,6 +271,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPCATEGORY_ENUM 'E'
#define TYPCATEGORY_GEOMETRIC 'G'
#define TYPCATEGORY_NETWORK 'I' /* think INET */
+#define TYPCATEGORY_MULTIRANGE 'M'
#define TYPCATEGORY_NUMERIC 'N'
#define TYPCATEGORY_PSEUDOTYPE 'P'
#define TYPCATEGORY_RANGE 'R'
@@ -285,7 +287,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#endif /* EXPOSE_TO_CLIENT_CODE */
@@ -344,4 +347,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index c8df5bff9f..ffdd90d69c 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -154,6 +154,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -179,6 +180,8 @@ extern void free_attstatsslot(AttStatsSlot *sslot);
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..ed2e19aafa
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,72 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ char vl_len_[4]; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType * range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType * make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType * make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 11ed19ccfd..521710d71f 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 918765cc99..32603eeb58 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -66,6 +66,7 @@ enum SysCacheIdentifier
INDEXRELID,
LANGNAME,
LANGOID,
+ MULTIRANGETYPE,
NAMESPACENAME,
NAMESPACEOID,
OPERNAMENSP,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index 04bf28180d..eda39881ef 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -99,6 +99,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
/*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype;
+
+ /*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
*/
@@ -141,6 +146,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 3fb8fc7bad..f575f882a2 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -511,6 +511,8 @@ do_compile(FunctionCallInfo fcinfo,
rettypeid = INT4ARRAYOID;
else if (rettypeid == ANYRANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2494,6 +2496,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYRANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..5749537635
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,292 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index c19740e5db..916e1016b5 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -206,6 +213,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -224,6 +233,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -329,13 +340,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
@@ -343,16 +355,19 @@ ORDER BY 2;
2502 | anyarray_recv
2312 | anyelement_in
3504 | anyenum_in
+ 8002 | anymultirange_in
2777 | anynonarray_in
3832 | anyrange_in
750 | array_in
2400 | array_recv
3506 | enum_in
3532 | enum_recv
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(13 rows)
+(16 rows)
-- Look for functions that accept cstring and are neither datatype input
-- functions nor encoding conversion functions. It's almost never a good
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index cd7fc03b04..8ef9595c37 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -193,18 +193,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -238,17 +239,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -316,18 +318,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -361,17 +364,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -629,3 +633,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.mltrngtypid
+FROM pg_range p1
+WHERE p1.mltrngtypid IS NULL OR p1.mltrngtypid = 0;
+ rngtypid | rngsubtype | mltrngtypid
+----------+------------+-------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index fc0f14122b..c8c1cd0671 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 68ac56acdb..35155436f0 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..8651ba3f3d
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,65 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 624bea46ce..23932cfef0 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -273,13 +284,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- Look for functions that accept cstring and are neither datatype input
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fed413bf98..b102c01bbc 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -151,7 +151,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -180,7 +180,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -232,7 +232,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -261,7 +261,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -465,3 +465,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.mltrngtypid
+FROM pg_range p1
+WHERE p1.mltrngtypid IS NULL OR p1.mltrngtypid = 0;
--
2.11.0
[application/octet-stream] v5-0003-multirange-pg_dump.patch (1.6K, ../../CA+renyW9KU=1mxyoNMuHPnBx4j5eFw+KKPgEMLcbKRUVekrPoA@mail.gmail.com/5-v5-0003-multirange-pg_dump.patch)
download | inline diff:
From 24297040a8209b11eb533baa63cb6f2b4fc7c51a Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:37 -0700
Subject: [PATCH v5 3/4] multirange pg_dump
---
src/bin/pg_dump/pg_dump.c | 7 ++++++-
src/bin/pg_dump/pg_dump.h | 1 +
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index bf69adc2f4..db84e79e11 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1583,7 +1583,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4923,6 +4923,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 7b2c1524a5..e27a752b6d 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -175,6 +175,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
--
2.11.0
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-11-19 09:16 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Pavel Stehule @ 2019-11-19 09:16 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
Hi
čt 7. 11. 2019 v 3:36 odesílatel Paul A Jungwirth <
pj@illuminatedcomputing.com> napsal:
> On Wed, Nov 6, 2019 at 3:02 PM Paul A Jungwirth
> <pj@illuminatedcomputing.com> wrote:
> > On Thu, Sep 26, 2019 at 2:13 PM Alvaro Herrera <alvherre@2ndquadrant.com>
> wrote:
> > > Hello Paul, I've started to review this patch. Here's a few minor
> > > things I ran across -- mostly compiler warnings (is my compiler too
> > > ancient?).
> > I just opened this thread to post a rebased set patches (especially
> > because of the `const` additions to range functions). Maybe it's not
> > that helpful since they don't include your changes yet but here they
> > are anyway. I'll post some more with your changes shortly.
>
> Here is another batch of patches incorporating your improvements. It
> seems like almost all the warnings were about moving variable
> declarations above any other statements. For some reason I don't get
> warnings about that on my end (compiling on OS X):
>
> platter:postgres paul$ gcc --version
> Configured with:
> --prefix=/Applications/Xcode.app/Contents/Developer/usr
> --with-gxx-include-dir=/usr/include/c++/4.2.1
> Apple clang version 11.0.0 (clang-1100.0.33.12)
> Target: x86_64-apple-darwin18.6.0
> Thread model: posix
> InstalledDir:
> /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
>
> For configure I'm saying this:
>
> ./configure CFLAGS=-ggdb 5-Og -g3 -fno-omit-frame-pointer
> --enable-tap-tests --enable-cassert --enable-debug
> --prefix=/Users/paul/local
>
> Any suggestions to get better warnings? On my other patch I got
> feedback about the very same kind. I could just compile on Linux but
> it's nice to work on this away from my desk on the laptop. Maybe
> installing a real gcc is the way to go.
>
I tested last patches. I found some issues
1. you should not to try patch catversion.
2. there is warning
parse_coerce.c: In function ‘enforce_generic_type_consistency’:
parse_coerce.c:1975:11: warning: ‘range_typelem’ may be used uninitialized
in this function [-Wmaybe-uninitialized]
1975 | else if (range_typelem != elem_typeid)
3. there are problems with pg_upgrade. Regress tests fails
command:
"/home/pavel/src/postgresql.master/tmp_install/usr/local/pgsql/bin/pg_restore"
--host /home/pavel/src/postgresql.master/src/b
pg_restore: connecting to database for restore
pg_restore: creating DATABASE "regression"
pg_restore: connecting to new database "regression"
pg_restore: connecting to database "regression" as user "pavel"
pg_restore: creating DATABASE PROPERTIES "regression"
pg_restore: connecting to new database "regression"
pg_restore: connecting to database "regression" as user "pavel"
pg_restore: creating pg_largeobject "pg_largeobject"
pg_restore: creating SCHEMA "fkpart3"
pg_restore: creating SCHEMA "fkpart4"
pg_restore: creating SCHEMA "fkpart5"
pg_restore: creating SCHEMA "fkpart6"
pg_restore: creating SCHEMA "mvtest_mvschema"
pg_restore: creating SCHEMA "regress_indexing"
pg_restore: creating SCHEMA "regress_rls_schema"
pg_restore: creating SCHEMA "regress_schema_2"
pg_restore: creating SCHEMA "testxmlschema"
pg_restore: creating TRANSFORM "TRANSFORM FOR integer LANGUAGE "sql""
pg_restore: creating TYPE "public.aggtype"
pg_restore: creating TYPE "public.arrayrange"
pg_restore: while PROCESSING TOC:
pg_restore: from TOC entry 1653; 1247 17044 TYPE arrayrange pavel
pg_restore: error: could not execute query: ERROR: pg_type array OID value
not set when in binary upgrade mode
Command was:.
-- For binary upgrade, must preserve pg_type oid
SELECT
pg_catalog.binary_upgrade_set_next_pg_type_oid('17044'::pg_catalog.oid);
-- For binary upgrade, must preserve pg_type array oid
SELECT
pg_catalog.binary_upgrade_set_next_array_pg_type_oid('17045'::pg_catalog.oid);
CREATE TYPE "public"."arrayrange" AS RANGE (
subtype = integer[]
);
4. there is a problem with doc
echo "<!ENTITY version \"13devel\">"; \
echo "<!ENTITY majorversion \"13\">"; \
} > version.sgml
'/usr/bin/perl' ./mk_feature_tables.pl YES
../../../src/backend/catalog/sql_feature_packages.txt
../../../src/backend/catalog/sql_features.txt > features-supported.sgml
'/usr/bin/perl' ./mk_feature_tables.pl NO
../../../src/backend/catalog/sql_feature_packages.txt
../../../src/backend/catalog/sql_features.txt > features-unsupported.sgml
'/usr/bin/perl' ./generate-errcodes-table.pl
../../../src/backend/utils/errcodes.txt > errcodes-table.sgml
'/usr/bin/perl' ./generate-keywords-table.pl . > keywords-table.sgml
/usr/bin/xmllint --path . --noout --valid postgres.sgml
extend.sgml:281: parser error : Opening and ending tag mismatch: para line
270 and type
type of the ranges in an </type>anymultirange</type>.
^
extend.sgml:281: parser error : Opening and ending tag mismatch: sect2 line
270 and type
type of the ranges in an </type>anymultirange</type>.
^
extend.sgml:282: parser error : Opening and ending tag mismatch: sect1 line
270 and para
</para>
^
extend.sgml:324: parser error : Opening and ending tag mismatch: chapter
line 270 and sect2
</sect2>
^
I am not sure how much is correct to use <literallayout class="monospaced">
in doc. It is used for ranges, and multiranges, but no in other places
All other looks well
Pavel
>
> Thanks,
> Paul
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-11-20 05:49 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2019-11-20 05:49 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Tue, Nov 19, 2019 at 1:17 AM Pavel Stehule <pavel.stehule@gmail.com> wrote:
> Hi
> I tested last patches. I found some issues
Thank you for the review!
> 1. you should not to try patch catversion.
I've seen discussion on pgsql-hackers going both ways, but I'll leave
it out of future patches. :-)
> 2. there is warning
>
> parse_coerce.c: In function ‘enforce_generic_type_consistency’:
> parse_coerce.c:1975:11: warning: ‘range_typelem’ may be used uninitialized in this function [-Wmaybe-uninitialized]
> 1975 | else if (range_typelem != elem_typeid)
Fixed locally, will include in my next patch.
> 3. there are problems with pg_upgrade. Regress tests fails
> . . .
> pg_restore: while PROCESSING TOC:
> pg_restore: from TOC entry 1653; 1247 17044 TYPE arrayrange pavel
> pg_restore: error: could not execute query: ERROR: pg_type array OID value not set when in binary upgrade mode
I see what's going on here. (Sorry if this verbose explanation is
obvious; it's as much for me as for anyone.) With pg_upgrade the
values of pg_type.oid and pg_type.typarray must be the same before &
after. For built-in types there's no problem, because those are fixed
by pg_type.dat. But for user-defined types we have to take extra steps
to make sure they don't change. CREATE TYPE always uses two oids: one
for the type and one for the type's array type. But now when you
create a range type we use *four*: the range type, the array of that
range, the multirange type, and the array of that multirange.
Currently when you run pg_dump in "binary mode" (i.e. as part of
pg_upgrade) it includes calls to special functions to set the next oid
to use for pg_type.oid and pg_type.typarray. Then CREATE TYPE also has
special "binary mode" code to check those variables and use those oids
(e.g. AssignTypeArrayOid). After using them once it sets them back to
InvalidOid so it doesn't keep using them. So I guess I need to add
code to pg_dump so that it also outputs calls to two new special
functions that similarly set the oid to use for the next multirange
and multirange[]. For v12->v13 it will chose high-enough oids like we
do already for arrays of domains. (For other upgrades it will use the
existing value.) And then I can change the CREATE TYPE code to check
those pre-set values when obtaining the next oid. Does that sound like
the right approach here?
> 4. there is a problem with doc
>
> extend.sgml:281: parser error : Opening and ending tag mismatch: para line 270 and type
> type of the ranges in an </type>anymultirange</type>.
Hmm, yikes, I'll fix that!
> I am not sure how much is correct to use <literallayout class="monospaced"> in doc. It is used for ranges, and multiranges, but no in other places
I could use some advice here. Many operators seem best presented in
groups of four, where only their parameter types change, for example:
int8range < int8range
int8range < int8multirange
int8multirange < int8range
int8multirange < int8multirange
All I really want is to show those separated by line breaks. I
couldn't find any other examples of that happening inside a table cell
though (i.e. inside <row><entry></entry></row>). What is the best way
to do that?
Thanks,
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-11-20 19:32 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-21 09:06 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-30 02:10 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 3 replies; 152+ messages in thread
From: Paul A Jungwirth @ 2019-11-20 19:32 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Tue, Nov 19, 2019 at 9:49 PM Paul A Jungwirth
<pj@illuminatedcomputing.com> wrote:
>
> On Tue, Nov 19, 2019 at 1:17 AM Pavel Stehule <pavel.stehule@gmail.com> wrote:
> > Hi
> > I tested last patches. I found some issues
>
> Thank you for the review!
Here is an updated patch series fixing the problems from that last
review. I would still like some direction about the doc formatting:
> > I am not sure how much is correct to use <literallayout class="monospaced"> in doc. It is used for ranges, and multiranges, but no in other places
>
> I could use some advice here. Many operators seem best presented in
> groups of four, where only their parameter types change, for example:
>
> int8range < int8range
> int8range < int8multirange
> int8multirange < int8range
> int8multirange < int8multirange
>
> All I really want is to show those separated by line breaks. I
> couldn't find any other examples of that happening inside a table cell
> though (i.e. inside <row><entry></entry></row>). What is the best way
> to do that?
Thanks,
Paul
Attachments:
[application/octet-stream] v6-0003-multirange-pg_dump.patch (17.8K, ../../CA+renyVNd0mGfbO-i2akkcQE9YrwNzAjVLchAcBEFZF+fiyJQw@mail.gmail.com/2-v6-0003-multirange-pg_dump.patch)
download | inline diff:
From b0f1a6ca6bf0981b905c1c647357f7cffbb1c7c2 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:37 -0700
Subject: [PATCH v6 3/4] multirange pg_dump
---
src/backend/commands/typecmds.c | 85 +++++++++++++++++--
src/backend/utils/adt/pg_upgrade_support.c | 22 +++++
src/bin/pg_dump/pg_dump.c | 130 ++++++++++++++++++++---------
src/bin/pg_dump/pg_dump.h | 1 +
src/include/catalog/binary_upgrade.h | 2 +
src/include/catalog/pg_proc.dat | 8 ++
src/include/commands/typecmds.h | 2 +
7 files changed, 205 insertions(+), 45 deletions(-)
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7a440cafd1..38948a049b 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -85,6 +85,8 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_multirange_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_multirange_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
@@ -1356,11 +1358,11 @@ DefineRange(CreateRangeStmt *stmt)
char *typeName;
Oid typeNamespace;
Oid typoid;
- Oid mltrngtypoid;
char *rangeArrayName;
char *multirangeTypeName;
char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeOid;
Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
@@ -1530,8 +1532,11 @@ DefineRange(CreateRangeStmt *stmt)
/* Allocate OID for array type */
rangeArrayOid = AssignTypeArrayOid();
+ /* Allocate OID for multirange type */
+ multirangeOid = AssignTypeMultirangeOid();
+
/* Allocate OID for multirange array type */
- multirangeArrayOid = AssignTypeArrayOid();
+ multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
address =
@@ -1573,7 +1578,7 @@ DefineRange(CreateRangeStmt *stmt)
multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
mltrngaddress =
- TypeCreate(InvalidOid, /* no predetermined type OID */
+ TypeCreate(multirangeOid, /* force assignment of this type OID */
multirangeTypeName, /* type name */
typeNamespace, /* namespace */
InvalidOid, /* relation oid (n/a here) */
@@ -1604,11 +1609,11 @@ DefineRange(CreateRangeStmt *stmt)
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
InvalidOid); /* type's collation (ranges never have one) */
- mltrngtypoid = mltrngaddress.objectId;
+ Assert(multirangeOid == mltrngaddress.objectId);
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff, mltrngtypoid);
+ rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
@@ -1671,7 +1676,7 @@ DefineRange(CreateRangeStmt *stmt)
InvalidOid, /* typmodin procedure - none */
InvalidOid, /* typmodout procedure - none */
F_ARRAY_TYPANALYZE, /* analyze procedure */
- mltrngtypoid, /* element type ID */
+ multirangeOid, /* element type ID */
true, /* yes this is an array type */
InvalidOid, /* no further array type */
InvalidOid, /* base type ID */
@@ -1688,7 +1693,7 @@ DefineRange(CreateRangeStmt *stmt)
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
makeMultirangeConstructors(multirangeTypeName, typeNamespace,
- mltrngtypoid, rangeArrayOid);
+ multirangeOid, rangeArrayOid);
pfree(multirangeTypeName);
pfree(multirangeArrayName);
@@ -2269,6 +2274,72 @@ AssignTypeArrayOid(void)
return type_array_oid;
}
+/*
+ * AssignTypeMultirangeOid
+ *
+ * Pre-assign the range type's multirange OID for use in pg_type.oid
+ */
+Oid
+AssignTypeMultirangeOid(void)
+{
+ Oid type_multirange_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_multirange_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
+
+ type_multirange_oid = binary_upgrade_next_multirange_pg_type_oid;
+ binary_upgrade_next_multirange_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_oid;
+}
+
+/*
+ * AssignTypeMultirangeArrayOid
+ *
+ * Pre-assign the range type's multirange array OID for use in pg_type.typarray
+ */
+Oid
+AssignTypeMultirangeArrayOid(void)
+{
+ Oid type_multirange_array_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_multirange_array_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
+
+ type_multirange_array_oid = binary_upgrade_next_multirange_array_pg_type_oid;
+ binary_upgrade_next_multirange_array_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_array_oid;
+}
+
/*-------------------------------------------------------------------
* DefineCompositeType
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 99db5ba389..d980b96f48 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -52,6 +52,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
}
Datum
+binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_multirange_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
+binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_multirange_array_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
binary_upgrade_set_next_toast_pg_type_oid(PG_FUNCTION_ARGS)
{
Oid typoid = PG_GETARG_OID(0);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index bf69adc2f4..4c65b047d2 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -274,7 +274,8 @@ static void dumpSearchPath(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type);
+ bool force_array_type,
+ bool include_multirange_type);
static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
static void binary_upgrade_set_pg_class_oids(Archive *fout,
@@ -1583,7 +1584,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4275,15 +4276,48 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
+static Oid get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+{
+ /*
+ * If the old version didn't assign an array type, but the new version
+ * does, we must select an unused type OID to assign. This currently
+ * only happens for domains, when upgrading pre-v11 to v11 and up.
+ *
+ * Note: local state here is kind of ugly, but we must have some,
+ * since we mustn't choose the same unused OID more than once.
+ */
+ static Oid next_possible_free_oid = FirstNormalObjectId;
+ PGresult *res;
+ bool is_dup;
+
+ do
+ {
+ ++next_possible_free_oid;
+ printfPQExpBuffer(upgrade_query,
+ "SELECT EXISTS(SELECT 1 "
+ "FROM pg_catalog.pg_type "
+ "WHERE oid = '%u'::pg_catalog.oid);",
+ next_possible_free_oid);
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+ is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
+ PQclear(res);
+ } while (is_dup);
+
+ return next_possible_free_oid;
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type)
+ bool force_array_type,
+ bool include_multirange_type)
{
PQExpBuffer upgrade_query = createPQExpBuffer();
PGresult *res;
Oid pg_type_array_oid;
+ Oid pg_type_multirange_oid;
+ Oid pg_type_multirange_array_oid;
appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
@@ -4304,33 +4338,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQclear(res);
if (!OidIsValid(pg_type_array_oid) && force_array_type)
- {
- /*
- * If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
- *
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
- */
- static Oid next_possible_free_oid = FirstNormalObjectId;
- bool is_dup;
-
- do
- {
- ++next_possible_free_oid;
- printfPQExpBuffer(upgrade_query,
- "SELECT EXISTS(SELECT 1 "
- "FROM pg_catalog.pg_type "
- "WHERE oid = '%u'::pg_catalog.oid);",
- next_possible_free_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
- is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
- PQclear(res);
- } while (is_dup);
-
- pg_type_array_oid = next_possible_free_oid;
- }
+ pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
if (OidIsValid(pg_type_array_oid))
{
@@ -4341,6 +4349,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
pg_type_array_oid);
}
+ /*
+ * Pre-set the multirange type oid and its own array type oid.
+ */
+ if (include_multirange_type)
+ {
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBuffer(upgrade_query,
+ "SELECT t.oid, t.typarray "
+ "FROM pg_catalog.pg_type t "
+ "JOIN pg_catalog.pg_range r "
+ "ON t.oid = r.mltrngtypid "
+ "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
+ pg_type_oid);
+
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+
+ pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+ pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
+
+ PQclear(res);
+ }
+ else
+ {
+ pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ }
+
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_oid);
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_array_oid);
+ }
+
destroyPQExpBuffer(upgrade_query);
}
@@ -4374,7 +4422,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
- pg_type_oid, false);
+ pg_type_oid, false, false);
if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
{
@@ -4923,6 +4971,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
@@ -10245,7 +10298,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
qualtypname);
@@ -10371,7 +10424,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, true);
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
qualtypname);
@@ -10477,7 +10530,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
qualtypname);
@@ -10682,7 +10735,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
@@ -10869,7 +10922,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- true); /* force array type */
+ true, /* force array type */
+ false); /* force multirange type */
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -11057,7 +11111,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
{
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
}
@@ -11331,7 +11385,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
stinfo->baseType->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
fmtQualifiedDumpable(stinfo));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 7b2c1524a5..e27a752b6d 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -175,6 +175,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 2927b7a4d3..2b6e87bb84 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,6 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_multirange_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_multirange_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 13ea94c024..d3cb8c8cb4 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10368,6 +10368,14 @@
proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
+{ oid => '8144', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
+{ oid => '8145', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
{ oid => '3585', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 8af0665f29..2b9115b6a0 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
+extern Oid AssignTypeMultirangeOid(void);
+extern Oid AssignTypeMultirangeArrayOid(void);
extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
--
2.11.0
[application/octet-stream] v6-0001-multirange-type-basics.patch (140.2K, ../../CA+renyVNd0mGfbO-i2akkcQE9YrwNzAjVLchAcBEFZF+fiyJQw@mail.gmail.com/3-v6-0001-multirange-type-basics.patch)
download | inline diff:
From f3e68d611f5df06a6eaeed192ce0d388cba1b192 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:25:39 -0700
Subject: [PATCH v6 1/4] multirange type basics
---
src/backend/catalog/pg_proc.c | 16 +-
src/backend/catalog/pg_range.c | 11 +-
src/backend/catalog/pg_type.c | 134 +++-
src/backend/commands/typecmds.c | 171 ++++-
src/backend/parser/parse_coerce.c | 303 +++++++-
src/backend/utils/adt/Makefile | 1 +
src/backend/utils/adt/multirangetypes.c | 966 ++++++++++++++++++++++++++
src/backend/utils/adt/pseudotypes.c | 25 +
src/backend/utils/adt/rangetypes.c | 155 ++++-
src/backend/utils/cache/lsyscache.c | 62 ++
src/backend/utils/cache/syscache.c | 11 +
src/backend/utils/cache/typcache.c | 109 ++-
src/backend/utils/fmgr/funcapi.c | 271 +++++++-
src/include/access/tupmacs.h | 4 +-
src/include/catalog/indexing.h | 3 +
src/include/catalog/pg_amop.dat | 22 +
src/include/catalog/pg_amproc.dat | 7 +
src/include/catalog/pg_opclass.dat | 4 +
src/include/catalog/pg_operator.dat | 33 +
src/include/catalog/pg_opfamily.dat | 4 +
src/include/catalog/pg_proc.dat | 77 ++
src/include/catalog/pg_range.dat | 15 +-
src/include/catalog/pg_range.h | 5 +-
src/include/catalog/pg_type.dat | 39 ++
src/include/catalog/pg_type.h | 8 +-
src/include/utils/lsyscache.h | 3 +
src/include/utils/multirangetypes.h | 72 ++
src/include/utils/rangetypes.h | 2 +
src/include/utils/syscache.h | 1 +
src/include/utils/typcache.h | 6 +
src/pl/plpgsql/src/pl_comp.c | 5 +
src/test/regress/expected/hash_func.out | 13 +
src/test/regress/expected/multirangetypes.out | 292 ++++++++
src/test/regress/expected/opr_sanity.out | 23 +-
src/test/regress/expected/type_sanity.out | 46 +-
src/test/regress/parallel_schedule | 3 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/hash_func.sql | 10 +
src/test/regress/sql/multirangetypes.sql | 65 ++
src/test/regress/sql/opr_sanity.sql | 18 +-
src/test/regress/sql/type_sanity.sql | 16 +-
41 files changed, 2895 insertions(+), 137 deletions(-)
create mode 100644 src/backend/utils/adt/multirangetypes.c
create mode 100644 src/include/utils/multirangetypes.h
create mode 100644 src/test/regress/expected/multirangetypes.out
create mode 100644 src/test/regress/sql/multirangetypes.sql
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index ef009ad2bc..80cc0721a5 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -192,6 +192,7 @@ ProcedureCreate(const char *procedureName,
genericInParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericInParam = true;
anyrangeInParam = true;
break;
@@ -219,6 +220,7 @@ ProcedureCreate(const char *procedureName,
genericOutParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericOutParam = true;
anyrangeOutParam = true;
break;
@@ -231,10 +233,10 @@ ProcedureCreate(const char *procedureName,
/*
* Do not allow polymorphic return type unless at least one input argument
- * is polymorphic. ANYRANGE return type is even stricter: must have an
- * ANYRANGE input (since we can't deduce the specific range type from
- * ANYELEMENT). Also, do not allow return type INTERNAL unless at least
- * one input argument is INTERNAL.
+ * is polymorphic. ANYRANGE and ANYMULTIRANGE return types are even
+ * stricter: must have an ANYRANGE or ANYMULTIRANGE input (since we can't
+ * deduce the specific range type from ANYELEMENT). Also, do not allow
+ * return type INTERNAL unless at least one input argument is INTERNAL.
*/
if ((IsPolymorphicType(returnType) || genericOutParam)
&& !genericInParam)
@@ -243,12 +245,12 @@ ProcedureCreate(const char *procedureName,
errmsg("cannot determine result data type"),
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
- if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
- !anyrangeInParam)
+ if ((returnType == ANYRANGEOID || returnType == ANYMULTIRANGEOID
+ || anyrangeOutParam) && !anyrangeInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
- errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
+ errdetail("A function returning \"anyrange\" or \"anymultirange\" must have at least one \"anyrange\" or \"anymultirange\" argument.")));
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
ereport(ERROR,
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index e6e138babd..d914f04858 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_mltrngtypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,13 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index a8c1de511f..5d962ed302 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -36,6 +36,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static int makeUniqueTypeName(char *dest, const char *typeName, size_t namelen,
+ Oid typeNamespace, bool tryOriginalName);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -785,34 +788,10 @@ makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
char *arr = (char *) palloc(NAMEDATALEN);
int namelen = strlen(typeName);
- Relation pg_type_desc;
- int i;
-
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- pg_type_desc = table_open(TypeRelationId, AccessShareLock);
-
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
-
- table_close(pg_type_desc, AccessShareLock);
+ int underscores;
- if (i >= NAMEDATALEN - 1)
+ underscores = makeUniqueTypeName(arr, typeName, namelen, typeNamespace, false);
+ if (underscores >= NAMEDATALEN - 1)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -883,3 +862,104 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * the caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char mltrng[NAMEDATALEN];
+ char *mltrngunique = (char *) palloc(NAMEDATALEN);
+ int namelen = strlen(rangeTypeName);
+ int rangelen;
+ char *rangestr;
+ int rangeoffset;
+ int underscores;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ strlcpy(mltrng, rangeTypeName, NAMEDATALEN);
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ rangeoffset = rangestr - rangeTypeName;
+ rangelen = strlen(rangestr);
+ strlcpy(mltrng + rangeoffset, "multi", NAMEDATALEN - rangeoffset);
+ strlcpy(mltrng + rangeoffset + 5, rangestr, NAMEDATALEN - rangeoffset - 5);
+ namelen += 5;
+ }
+ else
+ {
+ strlcpy(mltrng + namelen, "multirange", NAMEDATALEN - namelen);
+ namelen += 10;
+ }
+
+ underscores = makeUniqueTypeName(mltrngunique, mltrng, namelen, typeNamespace, true);
+ if (underscores >= NAMEDATALEN - 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mltrngunique;
+}
+
+/*
+ * makeUniqueTypeName: Prepend underscores as needed until we make a name that
+ * doesn't collide with anything. Tries the original typeName if requested.
+ *
+ * Returns the number of underscores added.
+ */
+int
+makeUniqueTypeName(char *dest, const char *typeName, size_t namelen, Oid typeNamespace,
+ bool tryOriginalName)
+{
+ Relation pg_type_desc;
+ int i;
+
+ pg_type_desc = table_open(TypeRelationId, AccessShareLock);
+
+ for (i = 0; i < NAMEDATALEN - 1; i++)
+ {
+ if (i == 0)
+ {
+ if (tryOriginalName &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ {
+ strcpy(dest, typeName);
+ break;
+ }
+
+ }
+ else
+ {
+ dest[i - 1] = '_';
+ if (i + namelen < NAMEDATALEN)
+ strcpy(dest + i, typeName);
+ else
+ {
+ strlcpy(dest + i, typeName, NAMEDATALEN);
+ truncate_identifier(dest, NAMEDATALEN, false);
+ }
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ break;
+ }
+ }
+
+ table_close(pg_type_desc, AccessShareLock);
+
+ return i;
+}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 89887b8fd7..14a6857062 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -88,6 +88,8 @@ Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeArrayOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -811,7 +813,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1353,8 +1356,12 @@ DefineRange(CreateRangeStmt *stmt)
char *typeName;
Oid typeNamespace;
Oid typoid;
+ Oid mltrngtypoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1371,6 +1378,7 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1522,6 +1530,9 @@ DefineRange(CreateRangeStmt *stmt)
/* Allocate OID for array type */
rangeArrayOid = AssignTypeArrayOid();
+ /* Allocate OID for multirange array type */
+ multirangeArrayOid = AssignTypeArrayOid();
+
/* Create the pg_type entry */
address =
TypeCreate(InvalidOid, /* no predetermined type OID */
@@ -1557,9 +1568,47 @@ DefineRange(CreateRangeStmt *stmt)
InvalidOid); /* type's collation (ranges never have one) */
Assert(typoid == address.objectId);
+ /* Create the multirange that goes with it */
+
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(InvalidOid, /* no predetermined type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_MULTIRANGE, /* type-category (multirange type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ mltrngtypoid = mltrngaddress.objectId;
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, mltrngtypoid);
/*
* Create the array type that goes with it.
@@ -1600,8 +1649,49 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ mltrngtypoid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ mltrngtypoid, rangeArrayOid);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1679,6 +1769,83 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeArrayOid)
+{
+ static const char *const prosrc[2] = {"multirange_constructor0",
+ "multirange_constructor1"};
+ static const int pronargs[2] = {0, 1};
+
+ Oid constructorArgTypes[0];
+ ObjectAddress myself,
+ referenced;
+ int i;
+
+ constructorArgTypes[0] = rangeArrayOid;
+
+ Datum allParamTypes[1] = {ObjectIdGetDatum(rangeArrayOid)};
+ ArrayType *allParameterTypes = construct_array(allParamTypes, 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ Datum constructorAllParamTypes[2] = {PointerGetDatum(NULL), PointerGetDatum(allParameterTypes)};
+
+ Datum paramModes[1] = {CharGetDatum(FUNC_PARAM_VARIADIC)};
+ ArrayType *parameterModes = construct_array(paramModes, 1, CHAROID,
+ 1, true, 'c');
+ Datum constructorParamModes[2] = {PointerGetDatum(NULL), PointerGetDatum(parameterModes)};
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ for (i = 0; i < lengthof(prosrc); i++)
+ {
+ oidvector *constructorArgTypesVector;
+
+ constructorArgTypesVector = buildoidvector(constructorArgTypes,
+ pronargs[i]);
+
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace, /* namespace */
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR, /* language validator */
+ prosrc[i], /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ constructorArgTypesVector, /* parameterTypes */
+ constructorAllParamTypes[i], /* allParameterTypes */
+ constructorParamModes[i], /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+
+ /*
+ * Make the constructors internally-dependent on the multirange type
+ * so that they go away silently when the type is dropped. Note that
+ * pg_dump depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ }
+}
/*
* Find suitable I/O functions for a type.
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index a6c51a95da..4c7a5f7fbe 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -187,18 +187,20 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
- targetTypeId == ANYRANGEOID)
+ targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call anyarray_in,
- * anyenum_in, or anyrange_in, which will produce an error. Also, if
- * what we have is a domain over array, enum, or range, we have to
- * relabel it to its base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * anyarray_in, anyenum_in, anyrange_in, or anymultirange_in, which
+ * will produce an error. Also, if what we have is a domain over
+ * array, enum, range, or multirange, we have to relabel it to its
+ * base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1482,6 +1484,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid array_typelem;
Oid range_typeid = InvalidOid;
Oid range_typelem;
+ Oid multirange_typeid = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
@@ -1528,6 +1532,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1580,6 +1593,37 @@ check_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+ }
+
if (have_anynonarray)
{
/* require the element type to not be an array or domain over array */
@@ -1681,13 +1725,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid array_typelem;
- Oid range_typelem;
+ Oid range_typelem = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
+ bool have_anyrange = (rettype == ANYRANGEOID);
+ bool have_anymultirange = (rettype == ANYMULTIRANGEOID);
/*
* Loop through the arguments to see if we have any that are polymorphic.
@@ -1745,7 +1793,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else if (decl_type == ANYRANGEOID)
{
- have_generics = true;
+ have_generics = have_anyrange = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
@@ -1763,6 +1811,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ have_generics = have_anymultirange = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
}
/*
@@ -1813,9 +1881,12 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- if (range_typeid == ANYRANGEOID && !have_anyelement)
+ if (range_typeid == ANYRANGEOID && !have_anyelement && !have_anymultirange)
{
- /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
+ /*
+ * Special case for ANYRANGE input: okay iff no ANYELEMENT or
+ * ANYMULTIRANGE
+ */
range_typelem = ANYELEMENTOID;
}
else
@@ -1849,6 +1920,69 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Get the range type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ if (multirange_typeid == ANYMULTIRANGEOID && !have_anyrange && !have_anyelement)
+ {
+ /*
+ * Special case for ANYMULTIRANGE input: okay iff no ANYRANGE or
+ * ANYELEMENT
+ */
+ multirange_typelem = ANYRANGEOID;
+ }
+ else
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+ }
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ /*
+ * Infer the element type too if needed (if there is an ANYMULTIRANGE
+ * and ANYELEMENT, with no ANYRANGE).
+ */
+ if (!OidIsValid(elem_typeid))
+ {
+ elem_typeid = get_range_subtype(range_typeid);
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
+ }
+
if (!OidIsValid(elem_typeid))
{
if (allow_poly)
@@ -1856,6 +1990,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -1928,6 +2063,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -1959,6 +2105,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
@@ -2007,8 +2169,7 @@ resolve_generic_type(Oid declared_type,
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
- context_declared_type == ANYENUMOID ||
- context_declared_type == ANYRANGEOID)
+ context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
@@ -2020,11 +2181,37 @@ resolve_generic_type(Oid declared_type,
format_type_be(context_actual_type))));
return array_typeid;
}
+ else if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid range_typelem = get_range_subtype(context_base_type);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+ Oid range_typelem = get_range_subtype(multirange_typelem);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
- declared_type == ANYENUMOID ||
- declared_type == ANYRANGEOID)
+ declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
@@ -2052,6 +2239,19 @@ resolve_generic_type(Oid declared_type,
"anyrange", format_type_be(context_base_type))));
return range_typelem;
}
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ /* Use the element type corresponding to actual type */
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
@@ -2060,6 +2260,74 @@ resolve_generic_type(Oid declared_type,
return context_actual_type;
}
}
+ else if (declared_type == ANYRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
+ else
+ {
+ /* We can't infer a range from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
+ else if (declared_type == ANYMULTIRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typeid = get_range_multirange(context_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return multirange_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else
+ {
+ /* We can't infer a multirange from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
else
{
/* declared_type isn't polymorphic, so return it as-is */
@@ -2174,6 +2442,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..7118dd0034 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..5323a6a635
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,966 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/hashutils.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ Oid typiofunc; /* range type's I/O function */
+ Oid typioparam; /* range type's I/O parameter */
+ FmgrInfo proc; /* lookup result for typiofunc */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData * get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks either either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ Oid rngtypoid;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+ rngtypoid = rangetyp->type_id;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */ ;
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **) repalloc(ranges,
+ range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(
+ InputFunctionCall(&cache->proc, range_str_copy,
+ cache->typioparam, typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "Unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->proc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(
+ ReceiveFunctionCall(&cache->proc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range = RangeTypePGetDatum(ranges[i]);
+ range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
+ uint32 range_len = VARSIZE(range) - VARHDRSZ;
+ char *range_data = VARDATA(range);
+
+ pq_sendint32(buf, range_len);
+ pq_sendbytes(buf, range_data, range_len);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &cache->typiofunc);
+
+ if (!OidIsValid(cache->typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(cache->typiofunc, &cache->proc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ RangeType *range;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ bytelen += MAXALIGN(VARSIZE(range));
+ }
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ memcpy(ptr, range, VARSIZE(range));
+ ptr += MAXALIGN(VARSIZE(range));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of any ranges you like into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ * Returns the number of slots actually used,
+ * which may be less than input_range_count but never more.
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType * multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR, (errmsg("Can't construct multirange with a multi-dimensional array")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL element")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ ereport(ERROR, (errmsg("Zero-param multirange constructor shouldn't have arguments")));
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 5c886cfe96..94484b3373 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -185,6 +186,30 @@ anyrange_out(PG_FUNCTION_ARGS)
}
/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
+/*
* void_in - input routine for pseudo-type VOID.
*
* We allow this so that PL functions can return VOID without any special
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 461c428413..65f68614c1 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -1177,12 +1175,68 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
+ return cmp;
+}
+
+/* btree comparator */
+Datum
+range_cmp(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int cmp;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ cmp = range_cmp_internal(typcache, r1, r2);
+
PG_FREE_IF_COPY(r1, 0);
PG_FREE_IF_COPY(r2, 1);
PG_RETURN_INT32(cmp);
}
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ /* For b-tree use, empty ranges sort before all else */
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
/* inequality operators using the range_cmp function */
Datum
range_lt(PG_FUNCTION_ARGS)
@@ -1218,13 +1272,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1284,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1325,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1354,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1392,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1938,6 +2006,45 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
}
/*
+ * Compares two ranges so we can qsort them.
+ * This expects that you give qsort a RangeType **,
+ * so the RangeTypes can be in diverse locations,
+ * as long as you have a list of pointers to them all.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
+/*
* Build an empty range value of the type indicated by the typcache entry.
*/
RangeType *
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 27602fa49c..360a71427e 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2469,6 +2469,16 @@ type_is_range(Oid typid)
}
/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
+/*
* get_type_category_preferred
*
* Given the type OID, fetch its category and preferred-type status.
@@ -3150,6 +3160,58 @@ get_range_subtype(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->mltrngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/* ---------- PG_MULTIRANGE CACHE ---------- */
+
+/*
+ * get_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_multirange_subtype(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index d69c0ff813..6156ab72a1 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -508,6 +508,17 @@ static const struct cachedesc cacheinfo[] = {
},
4
},
+ {RangeRelationId, /* MULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_mltrngtypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{NamespaceRelationId, /* NAMESPACENAME */
NamespaceNameIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 11920db0d9..0630687b49 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -278,6 +278,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -294,6 +295,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -500,8 +504,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -538,7 +542,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -563,7 +567,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -588,7 +592,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -640,6 +644,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -684,6 +695,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -759,6 +777,16 @@ lookup_type_cache(Oid type_id, int flags)
}
/*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
+ /*
* If requested, get information about a domain type
*/
if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
@@ -871,6 +899,33 @@ load_rangetype_info(TypeCacheEntry *typentry)
/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Form_pg_range pg_range;
+ HeapTuple tup;
+ Oid rangetypeOid;
+
+ /* get information from pg_range */
+ tup = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(typentry->type_id));
+ /* should not fail, since we already checked typtype ... */
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+ pg_range = (Form_pg_range) GETSTRUCT(tup);
+
+ rangetypeOid = pg_range->rngtypid;
+
+ ReleaseSysCache(tup);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
+
+
+/*
* load_domaintype_info --- helper routine to set up domain constraint info
*
* Note: we assume we're called in a relatively short-lived context, so it's
@@ -1469,11 +1524,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1516,6 +1571,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 4688fbc50c..d65968d4eb 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -470,11 +470,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anynonarray = false;
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
Oid anycollation = InvalidOid;
int i;
@@ -500,12 +502,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
case ANYRANGEOID:
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_anymultirange_result = true;
+ break;
default:
break;
}
}
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/*
@@ -533,6 +538,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (!OidIsValid(anyrange_type))
anyrange_type = get_call_expr_argtype(call_expr, i);
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(anymultirange_type))
+ anymultirange_type = get_call_expr_argtype(call_expr, i);
+ break;
default:
break;
}
@@ -540,7 +549,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/* If nothing found, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -561,19 +570,122 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ Oid rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ anymultirange_type = mltrngtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
/* Enforce ANYNONARRAY if needed */
if (have_anynonarray && type_is_array(anyelement_type))
@@ -640,6 +752,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
default:
break;
}
@@ -664,9 +784,11 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
int inargno;
int i;
@@ -725,6 +847,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ have_anymultirange_result = true;
+ else
+ {
+ if (!OidIsValid(anymultirange_type))
+ {
+ anymultirange_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anymultirange_type))
+ return false;
+ }
+ argtypes[i] = anymultirange_type;
+ }
+ break;
default:
break;
}
@@ -734,12 +871,12 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
/* Done? */
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/* If no input polymorphics, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -760,19 +897,122 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ Oid rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ }
+ else
+ {
+ return false;
+ }
+ }
/* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */
@@ -792,6 +1032,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = anymultirange_type;
+ break;
default:
break;
}
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 23cf481e78..50b4c4428f 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -137,8 +137,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ef4445b017..ca9890ab6c 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -330,6 +330,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_mltrngtypid_index, 8001, on pg_range using btree(mltrngtypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 232557ee81..999493f94d 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 5e705019b4..b7cb2cb000 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -225,6 +225,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
@@ -385,6 +387,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index 2d575102ef..2e59cf9159 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index fa7dc96ece..4d319bb5b3 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3289,5 +3289,38 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'contsel', oprjoin => 'contjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 41e40d657a..536c10cb0c 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 58ea5b982b..7ffa047969 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9724,6 +9724,83 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index dd9baa2678..9fd781e7b5 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ mltrngtypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', mltrngtypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', mltrngtypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', mltrngtypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ mltrngtypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ mltrngtypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index b01a074d09..9cfc56d4d2 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -45,6 +45,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* subtype difference as a float8, or 0 */
regproc rngsubdiff BKI_LOOKUP(pg_proc);
+
+ /* OID of the range's multirange type */
+ Oid mltrngtypid BKI_LOOKUP(pg_type);
} FormData_pg_range;
/* ----------------
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index be49e00114..c95a38849a 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -486,6 +486,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -594,5 +628,10 @@
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 2a584b4b13..6a25fcc9bf 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -259,6 +259,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -270,6 +271,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPCATEGORY_ENUM 'E'
#define TYPCATEGORY_GEOMETRIC 'G'
#define TYPCATEGORY_NETWORK 'I' /* think INET */
+#define TYPCATEGORY_MULTIRANGE 'M'
#define TYPCATEGORY_NUMERIC 'N'
#define TYPCATEGORY_PSEUDOTYPE 'P'
#define TYPCATEGORY_RANGE 'R'
@@ -285,7 +287,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#endif /* EXPOSE_TO_CLIENT_CODE */
@@ -344,4 +347,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index c8df5bff9f..ffdd90d69c 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -154,6 +154,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -179,6 +180,8 @@ extern void free_attstatsslot(AttStatsSlot *sslot);
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..ed2e19aafa
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,72 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ char vl_len_[4]; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType * range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType * make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType * make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 11ed19ccfd..521710d71f 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 918765cc99..32603eeb58 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -66,6 +66,7 @@ enum SysCacheIdentifier
INDEXRELID,
LANGNAME,
LANGOID,
+ MULTIRANGETYPE,
NAMESPACENAME,
NAMESPACEOID,
OPERNAMENSP,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index 04bf28180d..eda39881ef 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -99,6 +99,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
/*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype;
+
+ /*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
*/
@@ -141,6 +146,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 3fb8fc7bad..f575f882a2 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -511,6 +511,8 @@ do_compile(FunctionCallInfo fcinfo,
rettypeid = INT4ARRAYOID;
else if (rettypeid == ANYRANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2494,6 +2496,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYRANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..5749537635
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,292 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index c19740e5db..916e1016b5 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -206,6 +213,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -224,6 +233,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -329,13 +340,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
@@ -343,16 +355,19 @@ ORDER BY 2;
2502 | anyarray_recv
2312 | anyelement_in
3504 | anyenum_in
+ 8002 | anymultirange_in
2777 | anynonarray_in
3832 | anyrange_in
750 | array_in
2400 | array_recv
3506 | enum_in
3532 | enum_recv
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(13 rows)
+(16 rows)
-- Look for functions that accept cstring and are neither datatype input
-- functions nor encoding conversion functions. It's almost never a good
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index cd7fc03b04..8ef9595c37 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -193,18 +193,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -238,17 +239,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -316,18 +318,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -361,17 +364,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -629,3 +633,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.mltrngtypid
+FROM pg_range p1
+WHERE p1.mltrngtypid IS NULL OR p1.mltrngtypid = 0;
+ rngtypid | rngsubtype | mltrngtypid
+----------+------------+-------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index d33a4e143d..5fc0673efa 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index f86f5c5682..98a9afbc7d 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..8651ba3f3d
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,65 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 624bea46ce..23932cfef0 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -273,13 +284,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- Look for functions that accept cstring and are neither datatype input
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fed413bf98..b102c01bbc 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -151,7 +151,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -180,7 +180,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -232,7 +232,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -261,7 +261,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -465,3 +465,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.mltrngtypid
+FROM pg_range p1
+WHERE p1.mltrngtypid IS NULL OR p1.mltrngtypid = 0;
--
2.11.0
[application/octet-stream] v6-0004-multirange-docs.patch (25.4K, ../../CA+renyVNd0mGfbO-i2akkcQE9YrwNzAjVLchAcBEFZF+fiyJQw@mail.gmail.com/4-v6-0004-multirange-docs.patch)
download | inline diff:
From fcc5d11d3f2176ab2a859af7c62a3208e52da909 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:47 -0700
Subject: [PATCH v6 4/4] multirange docs
---
doc/src/sgml/extend.sgml | 28 +++-
doc/src/sgml/func.sgml | 302 +++++++++++++++++++++++++++++++++++++++----
doc/src/sgml/rangetypes.sgml | 47 ++++++-
3 files changed, 342 insertions(+), 35 deletions(-)
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index a3046f22d0..7ea2f65778 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -229,9 +229,9 @@
</indexterm>
<para>
- Five pseudo-types of special interest are <type>anyelement</type>,
+ Six pseudo-types of special interest are <type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
- and <type>anyrange</type>,
+ <type>anyrange</type>, and <type>anymultirange</type>.
which are collectively called <firstterm>polymorphic types</firstterm>.
Any function declared using these types is said to be
a <firstterm>polymorphic function</firstterm>. A polymorphic function can
@@ -250,15 +250,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type>, the actual range type in
- the <type>anyrange</type> positions must be a range whose subtype is
- the same type appearing in the <type>anyelement</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -268,6 +268,20 @@
</para>
<para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type>, the actual range type in
+ the <type>anyrange</type> positions must be a range whose subtype is
+ the same type appearing in the <type>anyelement</type> positions.
+ The type <type>anymultirange</type> accepts a multirange
+ whose contents match the other polymorphic types.
+ That is it must hold ranges matching <type>anyrange</type>
+ and base type elements matching <type>anyelement</type> (if either of
+ those are present). If <type>anyarray</type> is present its elements
+ must match the base type of the <type>anyrange</type> and/or the base
+ type of the ranges in an <type>anymultirange</type>.
+ </para>
+
+ <para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
types are allowed. For example, a function declared as
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 28eb322f3f..54b428eb39 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14566,7 +14566,9 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14576,7 +14578,7 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14584,136 +14586,247 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>f</literal></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)</literallayout></entry>
<entry><literal>[5,20)</literal></entry>
</row>
<row>
+ <entry> <literal>@+</literal> </entry>
+ <entry>safe union</entry>
+ <entry>
+ <literallayout class="monospaced">numrange(5,10) @+ numrange(15,20)
+numrange(5,10) @+ '{[15,20)}'::nummultirange
+'{[5,10)}'::nummultirange @+ numrange(15,20)
+'{[5,10)}'::nummultirange @+ '{[15,20)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literal>{[5,10), [15,20)}</literal></entry>
+ </row>
+
+ <row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)</literallayout></entry>
<entry><literal>[10,15)</literal></entry>
</row>
<row>
+ <entry> <literal>@*</literal> </entry>
+ <entry>safe intersection</entry>
+ <entry>
+ <literallayout class="monospaced">int8range(5,15) @* int8range(10,20)
+int8range(5,15) @* '{[10,20)}'::int8multirange
+'{[5,15)}'::int8multirange @* int8range(10,20)
+'{[5,15)}'::int8multirange @* '{[10,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literal>{[10,15)}</literal></entry>
+ </row>
+
+ <row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)</literallayout></entry>
<entry><literal>[5,10)</literal></entry>
</row>
+ <row>
+ <entry> <literal>@-</literal> </entry>
+ <entry>safe difference</entry>
+ <entry>
+ <literallayout class="monospaced">int8range(5,20) @- int8range(10,15)
+int8range(5,20) @- '{[10,15)}'::int8multirange
+'{[5,20)}'::int8multirange @- int8range(10,15)
+'{[5,20)}'::int8multirange @- '{[10,15)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literal>{[5,10), [15,20)}</literal></entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -14729,19 +14842,28 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. The safe versions of union, intersection, and difference
+ return multiranges, so they can handle gaps without failing.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14793,6 +14915,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14804,6 +14937,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>isempty</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14815,6 +14959,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14826,6 +14981,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14837,6 +15003,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14848,6 +15025,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14859,6 +15047,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>range_merge</function>(<type>anyrange</type>, <type>anyrange</type>)
</literal>
</entry>
@@ -14867,16 +15066,27 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -15198,6 +15408,44 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>string_agg</primary>
</indexterm>
<function>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index 3a034d9b06..3e37b352fc 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -235,10 +242,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -272,6 +299,19 @@ SELECT int8range(1, 14, '(]');
SELECT numrange(NULL, 2.2);
</programlisting>
</para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
+</programlisting>
+ </para>
</sect2>
<sect2 id="rangetypes-discrete">
@@ -345,6 +385,11 @@ SELECT '[1.234, 5.678]'::floatrange;
</para>
<para>
+ When you define your own range your automatically get a corresponding
+ multirange type.
+ </para>
+
+ <para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
ordering that determines which values fall into a given range.
--
2.11.0
[application/octet-stream] v6-0002-multirange-operators-and-functions.patch (179.7K, ../../CA+renyVNd0mGfbO-i2akkcQE9YrwNzAjVLchAcBEFZF+fiyJQw@mail.gmail.com/5-v6-0002-multirange-operators-and-functions.patch)
download | inline diff:
From 10ee59a513c2982423f5163931e9d9e63d5028d3 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:20 -0700
Subject: [PATCH v6 2/4] multirange operators and functions
---
src/backend/catalog/pg_type.c | 2 -
src/backend/commands/typecmds.c | 6 +-
src/backend/parser/parse_coerce.c | 2 +
src/backend/utils/adt/multirangetypes.c | 1575 +++++++++++++++-
src/backend/utils/adt/rangetypes.c | 236 ++-
src/backend/utils/fmgr/funcapi.c | 97 +-
src/include/catalog/pg_aggregate.dat | 11 +
src/include/catalog/pg_amproc.dat | 5 +-
src/include/catalog/pg_operator.dat | 169 ++
src/include/catalog/pg_proc.dat | 185 ++
src/include/utils/multirangetypes.h | 34 +
src/include/utils/rangetypes.h | 31 +-
src/test/regress/expected/dependency.out | 1 +
src/test/regress/expected/multirangetypes.out | 2397 +++++++++++++++++++++++++
src/test/regress/expected/opr_sanity.out | 4 +-
src/test/regress/expected/rangetypes.out | 38 +-
src/test/regress/expected/sanity_check.out | 2 +
src/test/regress/sql/multirangetypes.sql | 607 +++++++
src/test/regress/sql/rangetypes.sql | 13 +
19 files changed, 5188 insertions(+), 227 deletions(-)
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 5d962ed302..de0bb24649 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -876,7 +876,6 @@ makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
char mltrng[NAMEDATALEN];
char *mltrngunique = (char *) palloc(NAMEDATALEN);
int namelen = strlen(rangeTypeName);
- int rangelen;
char *rangestr;
int rangeoffset;
int underscores;
@@ -892,7 +891,6 @@ makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
if (rangestr)
{
rangeoffset = rangestr - rangeTypeName;
- rangelen = strlen(rangestr);
strlcpy(mltrng + rangeoffset, "multi", NAMEDATALEN - rangeoffset);
strlcpy(mltrng + rangeoffset + 5, rangestr, NAMEDATALEN - rangeoffset - 5);
namelen += 5;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 14a6857062..7a440cafd1 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1783,13 +1783,11 @@ makeMultirangeConstructors(const char *name, Oid namespace,
"multirange_constructor1"};
static const int pronargs[2] = {0, 1};
- Oid constructorArgTypes[0];
+ Oid constructorArgTypes = rangeArrayOid;
ObjectAddress myself,
referenced;
int i;
- constructorArgTypes[0] = rangeArrayOid;
-
Datum allParamTypes[1] = {ObjectIdGetDatum(rangeArrayOid)};
ArrayType *allParameterTypes = construct_array(allParamTypes, 1, OIDOID,
sizeof(Oid), true, 'i');
@@ -1808,7 +1806,7 @@ makeMultirangeConstructors(const char *name, Oid namespace,
{
oidvector *constructorArgTypesVector;
- constructorArgTypesVector = buildoidvector(constructorArgTypes,
+ constructorArgTypesVector = buildoidvector(&constructorArgTypes,
pronargs[i]);
myself = ProcedureCreate(name, /* name: same as multirange type */
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 4c7a5f7fbe..49c75dc12d 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1918,6 +1918,8 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(range_typeid),
format_type_be(elem_typeid))));
}
+ else
+ range_typelem = InvalidOid;
}
/* Get the range type based on the multirange type, if we have one */
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 5323a6a635..7717acb3b1 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -91,7 +91,6 @@ multirange_in(PG_FUNCTION_ARGS)
Oid mltrngtypoid = PG_GETARG_OID(1);
Oid typmod = PG_GETARG_INT32(2);
TypeCacheEntry *rangetyp;
- Oid rngtypoid;
int32 ranges_seen = 0;
int32 range_count = 0;
int32 range_capacity = 8;
@@ -101,13 +100,12 @@ multirange_in(PG_FUNCTION_ARGS)
MultirangeType *ret;
MultirangeParseState parse_state;
const char *ptr = input_str;
- const char *range_str;
+ const char *range_str = NULL;
int32 range_str_len;
char *range_str_copy;
cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
rangetyp = cache->typcache->rngtype;
- rngtypoid = rangetyp->type_id;
/* consume whitespace */
while (*ptr != '\0' && isspace((unsigned char) *ptr))
@@ -351,9 +349,12 @@ multirange_send(PG_FUNCTION_ARGS)
for (i = 0; i < range_count; i++)
{
Datum range = RangeTypePGetDatum(ranges[i]);
+ uint32 range_len;
+ char *range_data;
+
range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
- uint32 range_len = VARSIZE(range) - VARHDRSZ;
- char *range_data = VARDATA(range);
+ range_len = VARSIZE(range) - VARHDRSZ;
+ range_data = VARDATA(range);
pq_sendint32(buf, range_len);
pq_sendbytes(buf, range_data, range_len);
@@ -726,169 +727,1547 @@ multirange_constructor0(PG_FUNCTION_ARGS)
}
-/* multirange, multirange -> bool functions */
+/* multirange, multirange -> multirange type functions */
-/* equality (internal version) */
-bool
-multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+/* multirange union */
+Datum
+range_union_range(PG_FUNCTION_ARGS)
{
- int32 range_count_1;
- int32 range_count_2;
- int32 i;
- RangeType **ranges1;
- RangeType **ranges2;
- RangeType *r1;
- RangeType *r2;
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ MultirangeType *mr = make_multirange(mltrngtypoid, typcache->rngtype, 1, &r2);
- /* Different types should be prevented by ANYMULTIRANGE matching rules */
- if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
- elog(ERROR, "multirange types do not match");
+ PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r1, mr));
+}
- multirange_deserialize(mr1, &range_count_1, &ranges1);
- multirange_deserialize(mr2, &range_count_2, &ranges2);
+Datum
+range_union_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
- if (range_count_1 != range_count_2)
- return false;
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
- for (i = 0; i < range_count_1; i++)
- {
- r1 = ranges1[i];
- r2 = ranges2[i];
+ PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r, mr));
+}
- if (!range_eq_internal(typcache->rngtype, r1, r2))
- return false;
- }
+Datum
+multirange_union_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
- return true;
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r, mr));
}
-/* equality */
Datum
-multirange_eq(PG_FUNCTION_ARGS)
+multirange_union_multirange(PG_FUNCTION_ARGS)
{
MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
- PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
}
-/* inequality (internal version) */
-bool
-multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+MultirangeType *
+range_union_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
{
- return (!multirange_eq_internal(typcache, mr1, mr2));
+ int32 range_count;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (RangeIsEmpty(r))
+ return mr;
+ if (MultirangeIsEmpty(mr))
+ return make_multirange(typcache->type_id, typcache->rngtype, 1, &r);
+
+ multirange_deserialize(mr, &range_count, &ranges1);
+
+ ranges2 = palloc0((range_count + 1) * sizeof(RangeType *));
+
+ memcpy(ranges2, ranges1, range_count * sizeof(RangeType *));
+ ranges2[range_count] = r;
+ return make_multirange(typcache->type_id, typcache->rngtype, range_count + 1, ranges2);
}
-/* inequality */
+/* multirange minus */
Datum
-multirange_ne(PG_FUNCTION_ARGS)
+range_minus_range(PG_FUNCTION_ARGS)
{
- MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
- MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
- typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
- PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+ if (RangeIsEmpty(r1) || RangeIsEmpty(r2))
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, rangetyp, 1, &r1));
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r1,
+ 1,
+ &r2));
}
-/* Btree support */
+Datum
+range_minus_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, rangetyp, 1, &r));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r,
+ range_count,
+ ranges));
+}
-/* btree comparator */
Datum
-multirange_cmp(PG_FUNCTION_ARGS)
+multirange_minus_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr) || RangeIsEmpty(r))
+ PG_RETURN_MULTIRANGE_P(mr);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count,
+ ranges,
+ 1,
+ &r));
+}
+
+Datum
+multirange_minus_multirange(PG_FUNCTION_ARGS)
{
MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
- int32 range_count_1;
- int32 range_count_2;
- int32 range_count_max;
- int32 i;
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
RangeType **ranges1;
RangeType **ranges2;
- RangeType *r1;
- RangeType *r2;
- TypeCacheEntry *typcache;
- int cmp = 0; /* If both are empty we'll use this. */
- /* Different types should be prevented by ANYMULTIRANGE matching rules */
- if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
- elog(ERROR, "multirange types do not match");
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
- typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
- multirange_deserialize(mr1, &range_count_1, &ranges1);
- multirange_deserialize(mr2, &range_count_2, &ranges2);
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
- /* Loop over source data */
- range_count_max = Max(range_count_1, range_count_2);
- for (i = 0; i < range_count_max; i++)
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1, int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
{
- /*
- * If one multirange is shorter, it's as if it had empty ranges at the
- * end to extend its length. An empty range compares earlier than any
- * other range, so the shorter multirange comes before the longer.
- * This is the same behavior as in other types, e.g. in strings 'aaa'
- * < 'aaaaaa'.
- */
- if (i >= range_count_1)
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
{
- cmp = -1;
- break;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
}
- if (i >= range_count_2)
+
+ while (r2 != NULL)
{
- cmp = 1;
- break;
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
}
- r1 = ranges1[i];
- r2 = ranges2[i];
- cmp = range_cmp_internal(typcache->rngtype, r1, r2);
- if (cmp != 0)
- break;
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
}
- PG_FREE_IF_COPY(mr1, 0);
- PG_FREE_IF_COPY(mr2, 1);
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
- PG_RETURN_INT32(cmp);
+/* multirange intersection */
+Datum
+range_intersect_range(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r1) || RangeIsEmpty(r2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r1,
+ 1,
+ &r2));
}
-/* inequality operators using the multirange_cmp function */
Datum
-multirange_lt(PG_FUNCTION_ARGS)
+range_intersect_multirange(PG_FUNCTION_ARGS)
{
- int cmp = multirange_cmp(fcinfo);
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr2);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count2;
+ RangeType **ranges2;
- PG_RETURN_BOOL(cmp < 0);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r1,
+ range_count2,
+ ranges2));
}
Datum
-multirange_le(PG_FUNCTION_ARGS)
+multirange_intersect_range(PG_FUNCTION_ARGS)
{
- int cmp = multirange_cmp(fcinfo);
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ RangeType **ranges1;
- PG_RETURN_BOOL(cmp <= 0);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || RangeIsEmpty(r2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ 1,
+ &r2));
}
Datum
-multirange_ge(PG_FUNCTION_ARGS)
+multirange_intersect_multirange(PG_FUNCTION_ARGS)
{
- int cmp = multirange_cmp(fcinfo);
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
- PG_RETURN_BOOL(cmp >= 0);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+ /*
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1, but one extra won't
+ * hurt.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
}
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
Datum
-multirange_gt(PG_FUNCTION_ARGS)
+range_agg_transfn(PG_FUNCTION_ARGS)
{
- int cmp = multirange_cmp(fcinfo);
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
- PG_RETURN_BOOL(cmp > 0);
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_multirange_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType * mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges1[0], ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1, MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
+ return false;
+
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
+ return false;
+ }
+
+ /* All ranges in mr2 are satisfied */
+ return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
}
/* Hash support */
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 65f68614c1..0651618363 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -429,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -450,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -473,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -483,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -493,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -503,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -513,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -955,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -967,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -991,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1099,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1108,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1130,54 +1180,83 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
}
-/* Btree support */
+/* range, range -> range, range functions */
-/* btree comparator */
-Datum
-range_cmp(PG_FUNCTION_ARGS)
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
{
- RangeType *r1 = PG_GETARG_RANGE_P(0);
- RangeType *r2 = PG_GETARG_RANGE_P(1);
- TypeCacheEntry *typcache;
RangeBound lower1,
lower2;
RangeBound upper1,
upper2;
bool empty1,
empty2;
- int cmp;
-
- check_stack_depth(); /* recurses when subtype is a range type */
-
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- /* For b-tree use, empty ranges sort before all else */
- if (empty1 && empty2)
- cmp = 0;
- else if (empty1)
- cmp = -1;
- else if (empty2)
- cmp = 1;
- else
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
{
- cmp = range_cmp_bounds(typcache, &lower1, &lower2);
- if (cmp == 0)
- cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
}
- return cmp;
+ return false;
+}
+
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
}
+
+/* Btree support */
+
/* btree comparator */
Datum
range_cmp(PG_FUNCTION_ARGS)
@@ -1207,7 +1286,7 @@ range_cmp(PG_FUNCTION_ARGS)
* Internal version of range_cmp
*/
int
-range_cmp_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
{
RangeBound lower1,
lower2;
@@ -1273,7 +1352,7 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
uint32
-hash_range_internal(TypeCacheEntry *typcache, RangeType *r)
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
uint32 result;
TypeCacheEntry *scache;
@@ -1343,7 +1422,7 @@ hash_range(PG_FUNCTION_ARGS)
}
uint64
-hash_range_extended_internal(TypeCacheEntry *typcache, RangeType *r, Datum seed)
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
{
uint64 result;
TypeCacheEntry *scache;
@@ -1838,6 +1917,21 @@ range_get_flags(const RangeType *range)
}
/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
+/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
* This is only needed in GiST operations, so we don't include a provision
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index d65968d4eb..25623461bb 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -573,18 +573,20 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
anyrange_type = rngtype;
-
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -628,18 +630,21 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
{
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
anyrange_type = rngtype;
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -660,21 +665,25 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
{
if (OidIsValid(anyrange_type))
{
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and range results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
return false;
anyelement_type = subtype;
- Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
- anyrange_type,
- ANYRANGEOID);
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent range and multirange results */
- Oid rngtype = get_multirange_subtype(mltrngtype);
+ rngtype = get_multirange_subtype(mltrngtype);
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
@@ -900,18 +909,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
anyrange_type = rngtype;
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -955,18 +967,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
{
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
anyrange_type = rngtype;
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -987,21 +1002,25 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
{
if (OidIsValid(anyrange_type))
{
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and range results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
return false;
anyelement_type = subtype;
- Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
- anyrange_type,
- ANYRANGEOID);
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent range and multirange results */
- Oid rngtype = get_multirange_subtype(mltrngtype);
+ rngtype = get_multirange_subtype(mltrngtype);
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index 242d843347..07a7211c15 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index b7cb2cb000..32965d6b3d 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -1210,12 +1210,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 4d319bb5b3..ade223b6f3 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3322,5 +3322,174 @@
oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8138', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anyrange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@+(anyrange,anyrange)',
+ oprcode => 'range_union_range' },
+{ oid => '8115', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@+(anymultirange,anyrange)',
+ oprcode => 'range_union_multirange' },
+{ oid => '8116', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@+(anyrange,anymultirange)',
+ oprcode => 'multirange_union_range' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union_multirange' },
+{ oid => '8140', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anyrange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcode => 'range_minus_range' },
+{ oid => '8121', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'range_minus_multirange' },
+{ oid => '8122', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus_range' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus_multirange' },
+{ oid => '8142', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anyrange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@*(anyrange,anyrange)',
+ oprcode => 'range_intersect_range' },
+{ oid => '8127', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@*(anymultirange,anyrange)',
+ oprcode => 'range_intersect_multirange' },
+{ oid => '8128', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@*(anyrange,anymultirange)',
+ oprcode => 'multirange_intersect_range' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect_multirange' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 7ffa047969..13ea94c024 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9607,6 +9607,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9656,6 +9660,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9743,9 +9754,165 @@
{ oid => '8007', descr => 'I/O',
proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
{ oid => '8008', descr => 'multirange typanalyze',
proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8137',
+ proname => 'range_union_range', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_union_range' },
+{ oid => '8112',
+ proname => 'range_union_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_union_multirange' },
+{ oid => '8113',
+ proname => 'multirange_union_range', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_union_range' },
+{ oid => '8114',
+ proname => 'multirange_union_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union_multirange' },
+{ oid => '8139',
+ proname => 'range_minus_range', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_minus_range' },
+{ oid => '8118',
+ proname => 'range_minus_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_minus_multirange' },
+{ oid => '8119',
+ proname => 'multirange_minus_range', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_minus_range' },
+{ oid => '8120',
+ proname => 'multirange_minus_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus_multirange' },
+{ oid => '8141',
+ proname => 'range_intersect_range', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_range' },
+{ oid => '8124',
+ proname => 'range_intersect_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_intersect_multirange' },
+{ oid => '8125',
+ proname => 'multirange_intersect_range', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_intersect_range' },
+{ oid => '8126',
+ proname => 'multirange_intersect_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_multirange' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
{ oid => '8045', descr => 'int4multirange constructor',
proname => 'int4multirange', proisstrict => 'f',
@@ -9801,6 +9968,24 @@
prorettype => 'int8multirange', proargtypes => '_int8range',
proallargtypes => '{_int8range}', proargmodes => '{v}',
prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
index ed2e19aafa..cc4f82b0ba 100644
--- a/src/include/utils/multirangetypes.h
+++ b/src/include/utils/multirangetypes.h
@@ -59,6 +59,40 @@ extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr
MultirangeType * mr2);
extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
MultirangeType * mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern MultirangeType * range_union_multirange_internal(TypeCacheEntry *typcache,
+ RangeType *r,
+ MultirangeType * mr);
+extern MultirangeType * multirange_minus_multirange_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType * multirange_intersect_multirange_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
/* assorted support functions */
extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 521710d71f..0bee724886 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -94,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -117,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -127,15 +141,20 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
- const RangeBound *b2);
+ const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
- const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+ const RangeBound *b2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..778699a961 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -141,6 +141,7 @@ owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
owner of type deptest_range
+owner of type deptest_multirange
owner of table deptest2
owner of sequence ss1
owner of type deptest_t
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
index 5749537635..2276d6d002 100644
--- a/src/test/regress/expected/multirangetypes.out
+++ b/src/test/regress/expected/multirangetypes.out
@@ -290,3 +290,2400 @@ select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
{[a,d)}
(1 row)
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT 'empty'::numrange @+ 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @+ nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @+ 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @+ nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @+ numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT 'empty'::numrange @+ nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @+ numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @+ nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(2,4);
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(3,4);
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT 'empty'::numrange @- 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @- nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT 'empty'::numrange @- numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @- 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @- numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(1,3);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(1,2);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(2,4);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(3,4);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(1,3));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,10) @- nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------------
+ {[2,4),[5,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(0,2);
+ ?column?
+----------
+ {[2,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(1,2);
+ ?column?
+----------
+ {[2,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(3,4);
+ ?column?
+----------------
+ {[1,3),[4,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(13,18);
+ ?column?
+----------
+ {[1,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) @- nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) @- nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT 'empty'::numrange @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,3);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,5);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(2,5);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,5) @* numrange(2,3);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,3);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,5);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(2,5);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @* numrange(2,3);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,2) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,5) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(2,5) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(2,3) @* '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange @* '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange @* '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select $1 @+ $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 916e1016b5..e818eaea79 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1101,13 +1101,15 @@ ORDER BY 1, 2;
?| | ?|
?|| | ?||
@ | ~
+ @* | @*
+ @+ | @+
@@ | @@
@@@ | @@@
| | |
~<=~ | ~>=~
~<~ | ~>~
~= | ~=
-(30 rows)
+(32 rows)
-- Likewise for negator pairs.
SELECT DISTINCT o1.oprname AS op1, o2.oprname AS op2
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 6fd16bddd1..1bfe426be6 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1284,7 +1302,7 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
@@ -1392,6 +1410,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1400,6 +1419,16 @@ select * from outparam_succeed(int4range(1,2));
[1,2) | foo
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1408,6 +1437,7 @@ select * from inoutparam_succeed(int4range(1,2));
2 | [1,2)
(1 row)
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
select * from table_succeed(123, int4range(1,11));
@@ -1420,14 +1450,14 @@ select * from table_succeed(123, int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 070de78e85..6f7fcf6326 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
onek|t
onek2|t
path_tbl|f
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
index 8651ba3f3d..b3ede18f96 100644
--- a/src/test/regress/sql/multirangetypes.sql
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -63,3 +63,610 @@ select textmultirange();
select textmultirange(textrange('a', 'c'));
select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT 'empty'::numrange @+ 'empty'::numrange;
+SELECT 'empty'::numrange @+ nummultirange();
+SELECT nummultirange() @+ 'empty'::numrange;
+SELECT nummultirange() @+ nummultirange();
+SELECT 'empty'::numrange @+ numrange(1,2);
+SELECT 'empty'::numrange @+ nummultirange(numrange(1,2));
+SELECT numrange(1,2) @+ nummultirange();
+SELECT nummultirange(numrange(1,2)) @+ 'empty'::numrange;
+SELECT numrange(1,2) @+ 'empty'::numrange;
+SELECT nummultirange() @+ numrange(1,2);
+SELECT nummultirange() @+ nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange();
+SELECT numrange(1,2) @+ numrange(1,2);
+SELECT numrange(1,2) @+ numrange(2,4);
+SELECT numrange(1,2) @+ numrange(3,4);
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT 'empty'::numrange @- 'empty'::numrange;
+SELECT 'empty'::numrange @- nummultirange();
+SELECT nummultirange() @- 'empty'::numrange;
+SELECT nummultirange() @- nummultirange();
+SELECT 'empty'::numrange @- numrange(1,2);
+SELECT 'empty'::numrange @- nummultirange(numrange(1,2));
+SELECT numrange(1,2) @- nummultirange();
+SELECT nummultirange(numrange(1,2)) @- 'empty'::numrange;
+SELECT 'empty'::numrange @- numrange(1,2);
+SELECT numrange(1,2) @- 'empty'::numrange;
+SELECT nummultirange() @- numrange(1,2);
+SELECT nummultirange() @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @- nummultirange();
+SELECT numrange(1,3) @- numrange(1,3);
+SELECT numrange(1,3) @- numrange(1,2);
+SELECT numrange(1,3) @- numrange(2,4);
+SELECT numrange(1,3) @- numrange(3,4);
+SELECT numrange(1,3) @- nummultirange(numrange(1,3));
+SELECT numrange(1,3) @- nummultirange(numrange(1,2));
+SELECT numrange(1,3) @- nummultirange(numrange(2,4));
+SELECT numrange(1,3) @- nummultirange(numrange(3,4));
+SELECT numrange(1,10) @- nummultirange(numrange(1,2), numrange(4,5));
+SELECT nummultirange(numrange(1,10)) @- numrange(0,2);
+SELECT nummultirange(numrange(1,10)) @- numrange(1,2);
+SELECT nummultirange(numrange(1,10)) @- numrange(3,4);
+SELECT nummultirange(numrange(1,10)) @- numrange(13,18);
+SELECT nummultirange(numrange(1,2), numrange(3,4)) @- nummultirange();
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) @- nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT 'empty'::numrange @* 'empty'::numrange;
+SELECT 'empty'::numrange @* nummultirange();
+SELECT nummultirange() @* 'empty'::numrange;
+SELECT nummultirange() @* nummultirange();
+SELECT 'empty'::numrange @* numrange(1,2);
+SELECT 'empty'::numrange @* nummultirange(numrange(1,2));
+SELECT numrange(1,2) @* 'empty'::numrange;
+SELECT numrange(1,2) @* nummultirange();
+SELECT nummultirange(numrange(1,2)) @* 'empty'::numrange;
+SELECT nummultirange() @* numrange(1,2);
+SELECT nummultirange() @* nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @* nummultirange();
+SELECT numrange(1,3) @* numrange(1,3);
+SELECT numrange(1,3) @* numrange(1,2);
+SELECT numrange(1,3) @* numrange(1,5);
+SELECT numrange(1,3) @* numrange(2,5);
+SELECT numrange(1,5) @* numrange(2,3);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,3);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,2);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,5);
+SELECT '{[1,3)}'::nummultirange @* numrange(2,5);
+SELECT '{[1,5)}'::nummultirange @* numrange(2,3);
+SELECT numrange(1,3) @* '{[1,3)}'::nummultirange;
+SELECT numrange(1,2) @* '{[1,3)}'::nummultirange;
+SELECT numrange(1,5) @* '{[1,3)}'::nummultirange;
+SELECT numrange(2,5) @* '{[1,3)}'::nummultirange;
+SELECT numrange(2,3) @* '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange @* '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange @* '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select $1 @+ $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 8960add976..32e18661d0 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -481,16 +485,25 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
--
2.11.0
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-11-21 09:06 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-11-21 20:15 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2 siblings, 1 reply; 152+ messages in thread
From: Pavel Stehule @ 2019-11-21 09:06 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
st 20. 11. 2019 v 20:32 odesílatel Paul A Jungwirth <
pj@illuminatedcomputing.com> napsal:
> On Tue, Nov 19, 2019 at 9:49 PM Paul A Jungwirth
> <pj@illuminatedcomputing.com> wrote:
> >
> > On Tue, Nov 19, 2019 at 1:17 AM Pavel Stehule <pavel.stehule@gmail.com>
> wrote:
> > > Hi
> > > I tested last patches. I found some issues
> >
> > Thank you for the review!
>
> Here is an updated patch series fixing the problems from that last
> review. I would still like some direction about the doc formatting:
>
>
yes, these bugs are fixed
there are not compilation's issues
tests passed
doc is ok
I have notes
1. the chapter should be renamed to "Range Functions and Operators" to
"Range and Multirange Functions and Operators"
But now the doc is not well readable - there is not clean, what functions
are for range type, what for multirange and what for both
2. I don't like introduction "safe" operators - now the basic operators are
doubled, and nobody without documentation will use @* operators.
It is not intuitive. I think is better to map this functionality to basic
operators +- * and implement it just for pairs (Multirange, Multirange) and
(Multirange, Range) if it is possible
It's same relation line Numeric X integer. There should not be introduced
new operators. If somebody need it for ranges, then he can use cast to
multirange, and can continue.
The "safe" operators can be implement on user space - but should not be
default solution.
3. There are not prepared casts -
postgres=# select int8range(10,15)::int8multirange;
ERROR: cannot cast type int8range to int8multirange
LINE 1: select int8range(10,15)::int8multirange;
^
There should be some a) fully generic solution, or b) possibility to build
implicit cast when any multirange type is created.
Regards
Pavel
> > > I am not sure how much is correct to use <literallayout
> class="monospaced"> in doc. It is used for ranges, and multiranges, but no
> in other places
> >
> > I could use some advice here. Many operators seem best presented in
> > groups of four, where only their parameter types change, for example:
> >
> > int8range < int8range
> > int8range < int8multirange
> > int8multirange < int8range
> > int8multirange < int8multirange
> >
> > All I really want is to show those separated by line breaks. I
> > couldn't find any other examples of that happening inside a table cell
> > though (i.e. inside <row><entry></entry></row>). What is the best way
> > to do that?
>
Personally I think it should be cleaned. Mainly if there is not visible
differences. But range related doc it uses, so it is consistent with it.
And then this is not big issue.
> Thanks,
> Paul
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-21 09:06 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-11-21 20:15 ` Paul Jungwirth <pj@illuminatedcomputing.com>
2019-11-22 05:21 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul Jungwirth @ 2019-11-21 20:15 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On 11/21/19 1:06 AM, Pavel Stehule wrote:
> 2. I don't like introduction "safe" operators - now the basic operators
> are doubled, and nobody without documentation will use @* operators.
>
> It is not intuitive. I think is better to map this functionality to
> basic operators +- * and implement it just for pairs (Multirange,
> Multirange) and (Multirange, Range) if it is possible
>
> It's same relation line Numeric X integer. There should not be
> introduced new operators. If somebody need it for ranges, then he can
> use cast to multirange, and can continue.
> [snip]
> 3. There are not prepared casts -
>
> postgres=# select int8range(10,15)::int8multirange;
> ERROR: cannot cast type int8range to int8multirange
> LINE 1: select int8range(10,15)::int8multirange;
> ^
> There should be some a) fully generic solution, or b) possibility to
> build implicit cast when any multirange type is created.
Okay, I like the idea of just having `range + range` and `multirange +
multirange`, then letting you cast between ranges and multiranges. The
analogy to int/numeric seems strong. I guess if you cast a multirange
with more than one element to a range it will raise an error. That will
let me clean up the docs a lot too.
Thanks!
--
Paul ~{:-)
pj@illuminatedcomputing.com
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-21 09:06 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-21 20:15 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
@ 2019-11-22 05:21 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-11-22 16:25 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Pavel Stehule @ 2019-11-22 05:21 UTC (permalink / raw)
To: Paul Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
čt 21. 11. 2019 v 21:15 odesílatel Paul Jungwirth <
pj@illuminatedcomputing.com> napsal:
> On 11/21/19 1:06 AM, Pavel Stehule wrote:
> > 2. I don't like introduction "safe" operators - now the basic operators
> > are doubled, and nobody without documentation will use @* operators.
> >
> > It is not intuitive. I think is better to map this functionality to
> > basic operators +- * and implement it just for pairs (Multirange,
> > Multirange) and (Multirange, Range) if it is possible
> >
> > It's same relation line Numeric X integer. There should not be
> > introduced new operators. If somebody need it for ranges, then he can
> > use cast to multirange, and can continue.
> > [snip]
> > 3. There are not prepared casts -
> >
> > postgres=# select int8range(10,15)::int8multirange;
> > ERROR: cannot cast type int8range to int8multirange
> > LINE 1: select int8range(10,15)::int8multirange;
> > ^
> > There should be some a) fully generic solution, or b) possibility to
> > build implicit cast when any multirange type is created.
>
> Okay, I like the idea of just having `range + range` and `multirange +
> multirange`, then letting you cast between ranges and multiranges. The
> analogy to int/numeric seems strong. I guess if you cast a multirange
> with more than one element to a range it will raise an error. That will
> let me clean up the docs a lot too.
>
I though about it, and I think so cast from multirange to range is useless,
minimally it should be explicit.
On second hand - from range to multirange should be implicit.
The original patch did
1. MR @x MR = MR
2. R @x R = MR
3. MR @x R = MR
I think so @1 & @3 has sense, but without introduction of special operator.
@2 is bad and can be solved by cast one or second operand.
Pavel
> Thanks!
>
> --
> Paul ~{:-)
> pj@illuminatedcomputing.com
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-21 09:06 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-21 20:15 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-11-22 05:21 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-11-22 16:25 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-22 16:57 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2019-11-22 16:25 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Thu, Nov 21, 2019 at 9:21 PM Pavel Stehule <pavel.stehule@gmail.com> wrote:
> I though about it, and I think so cast from multirange to range is useless, minimally it should be explicit.
I agree: definitely not implicit. If I think of a good reason for it
I'll add it, but otherwise I'll leave it out.
> On second hand - from range to multirange should be implicit.
Okay.
> The original patch did
>
> 1. MR @x MR = MR
> 2. R @x R = MR
> 3. MR @x R = MR
>
> I think so @1 & @3 has sense, but without introduction of special operator. @2 is bad and can be solved by cast one or second operand.
Yes. I like how #2 follows the int/numeric analogy: if you want a
numeric result from `int / int` you can say `int::numeric / int`.
So my understanding is that conventionally cast functions are named
after the destination type, e.g. int8multirange(int8range) would be
the function to cast an int8range to an int8multirange. And
int8range(int8multirange) would go the other way (if we do that). We
already use these names for the "constructor" functions, but I think
that is actually okay. For the multirange->range cast, the parameter
type & number are different, so there is no real conflict. For the
range->multirange cast, the parameter type is the same, and the
constructor function is variadic---but I think that's fine, because
the semantics are the same: build a multirange whose only element is
the given range:
regression=# select int8multirange(int8range(1,2));
int8multirange
----------------
{[1,2)}
(1 row)
Even the NULL handling is already what we want:
regression=# select int8multirange(null);
int8multirange
----------------
NULL
(1 row)
So I think it's fine, but I'm curious whether you see any problems
there? (I guess if there is a problem it's no big deal to name the
function something else....)
Thanks,
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-21 09:06 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-21 20:15 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-11-22 05:21 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-22 16:25 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-11-22 16:57 ` Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 0 replies; 152+ messages in thread
From: Pavel Stehule @ 2019-11-22 16:57 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
pá 22. 11. 2019 v 17:25 odesílatel Paul A Jungwirth <
pj@illuminatedcomputing.com> napsal:
> On Thu, Nov 21, 2019 at 9:21 PM Pavel Stehule <pavel.stehule@gmail.com>
> wrote:
> > I though about it, and I think so cast from multirange to range is
> useless, minimally it should be explicit.
>
> I agree: definitely not implicit. If I think of a good reason for it
> I'll add it, but otherwise I'll leave it out.
>
> > On second hand - from range to multirange should be implicit.
>
> Okay.
>
> > The original patch did
> >
> > 1. MR @x MR = MR
> > 2. R @x R = MR
> > 3. MR @x R = MR
> >
> > I think so @1 & @3 has sense, but without introduction of special
> operator. @2 is bad and can be solved by cast one or second operand.
>
> Yes. I like how #2 follows the int/numeric analogy: if you want a
> numeric result from `int / int` you can say `int::numeric / int`.
>
> So my understanding is that conventionally cast functions are named
> after the destination type, e.g. int8multirange(int8range) would be
> the function to cast an int8range to an int8multirange. And
> int8range(int8multirange) would go the other way (if we do that). We
> already use these names for the "constructor" functions, but I think
> that is actually okay. For the multirange->range cast, the parameter
> type & number are different, so there is no real conflict. For the
> range->multirange cast, the parameter type is the same, and the
> constructor function is variadic---but I think that's fine, because
> the semantics are the same: build a multirange whose only element is
> the given range:
>
> regression=# select int8multirange(int8range(1,2));
> int8multirange
> ----------------
> {[1,2)}
> (1 row)
>
> Even the NULL handling is already what we want:
>
> regression=# select int8multirange(null);
> int8multirange
> ----------------
> NULL
> (1 row)
>
> So I think it's fine, but I'm curious whether you see any problems
> there? (I guess if there is a problem it's no big deal to name the
> function something else....)
>
It looks well now. I am not sure about benefit of cast from MR to R if MR
has more than one values. But it can be there for completeness.
I think in this moment is not important to implement all functionality -
for start is good to implement basic functionality that can be good. It can
be enhanced step by step in next versions.
Pavel
>
> Thanks,
> Paul
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-11-30 02:10 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2 siblings, 0 replies; 152+ messages in thread
From: Alvaro Herrera @ 2019-11-30 02:10 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
Hi Paul
I'm starting to look at this again. Here's a few proposed changes to
the current code as I read along.
I noticed that 0001 does not compile on its own. It works as soon as I
add 0002. What this is telling me is that the current patch split is
not serving any goals; I think it's okay to merge them all into a single
commit. If you want to split it in smaller pieces, it needs to be stuff
that can be committed separately.
One possible candidate for that is the new makeUniqueTypeName function
you propose. I added this comment to explain what it does:
/*
- * makeUniqueTypeName: Prepend underscores as needed until we make a name that
- * doesn't collide with anything. Tries the original typeName if requested.
+ * makeUniqueTypeName
+ * Generate a unique name for a prospective new type
+ *
+ * Given a typeName of length namelen, produce a new name into dest (an output
+ * buffer allocated by caller, which must of length NAMEDATALEN) by prepending
+ * underscores, until a non-conflicting name results.
+ *
+ * If tryOriginalName, first try with zero underscores.
*
* Returns the number of underscores added.
*/
This seems a little too strange; why not have the function allocate its
output buffer instead, and return it? In order to support the case of
it failing to find an appropriate name, have it return NULL, for caller
to throw the "could not form ... name" error.
The attached 0001 simplifies makeMultirangeConstructors; I think it was
too baroque just because of it trying to imitate makeRangeConstructors;
it seems easier to just repeat the ProcedureCreate call than trying to
be clever. (makeRangeConstructors's comment is lying about the number
of constructors it creates after commit df73584431e7, BTW. But note
that the cruft in it to support doing it twice is not as much as in the
new code).
The other patches should be self-explanatory (I already submitted 0002
previously.)
I'll keep going over the rest of it. Thanks!
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
[text/x-diff] 0001-Simplify-makeMultirangeConstructors.patch (6.2K, ../../20191130021026.GA19259@alvherre.pgsql/2-0001-Simplify-makeMultirangeConstructors.patch)
download | inline diff:
From 6a5b230f741fb4272770d09750b57fdad225027e Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 28 Nov 2019 19:08:43 -0300
Subject: [PATCH 1/6] Simplify makeMultirangeConstructors
---
src/backend/commands/typecmds.c | 134 +++++++++++++++++++-------------
1 file changed, 80 insertions(+), 54 deletions(-)
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 38948a049b..1b012c9cad 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1784,70 +1784,96 @@ static void
makeMultirangeConstructors(const char *name, Oid namespace,
Oid multirangeOid, Oid rangeArrayOid)
{
- static const char *const prosrc[2] = {"multirange_constructor0",
- "multirange_constructor1"};
- static const int pronargs[2] = {0, 1};
-
- Oid constructorArgTypes = rangeArrayOid;
ObjectAddress myself,
referenced;
- int i;
-
- Datum allParamTypes[1] = {ObjectIdGetDatum(rangeArrayOid)};
- ArrayType *allParameterTypes = construct_array(allParamTypes, 1, OIDOID,
- sizeof(Oid), true, 'i');
- Datum constructorAllParamTypes[2] = {PointerGetDatum(NULL), PointerGetDatum(allParameterTypes)};
-
- Datum paramModes[1] = {CharGetDatum(FUNC_PARAM_VARIADIC)};
- ArrayType *parameterModes = construct_array(paramModes, 1, CHAROID,
- 1, true, 'c');
- Datum constructorParamModes[2] = {PointerGetDatum(NULL), PointerGetDatum(parameterModes)};
+ oidvector *argtypes;
+ Datum allParamTypes;
+ ArrayType *allParameterTypes;
+ Datum paramModes;
+ ArrayType *parameterModes;
referenced.classId = TypeRelationId;
referenced.objectId = multirangeOid;
referenced.objectSubId = 0;
- for (i = 0; i < lengthof(prosrc); i++)
- {
- oidvector *constructorArgTypesVector;
+ argtypes = buildoidvector(NULL, 0);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor0", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
- constructorArgTypesVector = buildoidvector(&constructorArgTypes,
- pronargs[i]);
+ /*
+ * Make the constructor internally-dependent on the multirange type so
+ * that they go away silently when the type is dropped. Note that pg_dump
+ * depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
- myself = ProcedureCreate(name, /* name: same as multirange type */
- namespace, /* namespace */
- false, /* replace */
- false, /* returns set */
- multirangeOid, /* return type */
- BOOTSTRAP_SUPERUSERID, /* proowner */
- INTERNALlanguageId, /* language */
- F_FMGR_INTERNAL_VALIDATOR, /* language validator */
- prosrc[i], /* prosrc */
- NULL, /* probin */
- PROKIND_FUNCTION,
- false, /* security_definer */
- false, /* leakproof */
- false, /* isStrict */
- PROVOLATILE_IMMUTABLE, /* volatility */
- PROPARALLEL_SAFE, /* parallel safety */
- constructorArgTypesVector, /* parameterTypes */
- constructorAllParamTypes[i], /* allParameterTypes */
- constructorParamModes[i], /* parameterModes */
- PointerGetDatum(NULL), /* parameterNames */
- NIL, /* parameterDefaults */
- PointerGetDatum(NULL), /* trftypes */
- PointerGetDatum(NULL), /* proconfig */
- InvalidOid, /* prosupport */
- 1.0, /* procost */
- 0.0); /* prorows */
+ pfree(argtypes);
- /*
- * Make the constructors internally-dependent on the multirange type
- * so that they go away silently when the type is dropped. Note that
- * pg_dump depends on this choice to avoid dumping the constructors.
- */
- recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
- }
+ argtypes = buildoidvector(&rangeArrayOid, 1);
+ allParamTypes = ObjectIdGetDatum(rangeArrayOid);
+ allParameterTypes = construct_array(&allParamTypes,
+ 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
+ parameterModes = construct_array(¶mModes, 1, CHAROID,
+ 1, true, 'c');
+
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor1", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(allParameterTypes), /* allParameterTypes */
+ PointerGetDatum(parameterModes), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+
+ pfree(argtypes);
+ pfree(allParameterTypes);
+ pfree(parameterModes);
}
/*
--
2.20.1
[text/x-diff] 0002-silence-compiler-warning.patch (778B, ../../20191130021026.GA19259@alvherre.pgsql/3-0002-silence-compiler-warning.patch)
download | inline diff:
From 589ef7f9a7f6debe767e7d3e0427941b349a3d13 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 28 Nov 2019 19:08:46 -0300
Subject: [PATCH 2/6] silence compiler warning
---
src/backend/parser/parse_coerce.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 49c75dc12d..b43b2dc612 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1592,6 +1592,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
}
}
+ else
+ range_typelem = InvalidOid; /* keep compiler quiet */
/* Get the element type based on the multirange type, if we have one */
if (OidIsValid(multirange_typeid))
--
2.20.1
[text/x-diff] 0003-Be-less-verbose-on-variable-names.patch (4.8K, ../../20191130021026.GA19259@alvherre.pgsql/4-0003-Be-less-verbose-on-variable-names.patch)
download | inline diff:
From b116b5c95f875361c01985b56c2f68d271a12e5e Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 28 Nov 2019 19:30:03 -0300
Subject: [PATCH 3/6] Be less verbose on variable names
---
src/backend/commands/typecmds.c | 23 +++++++++-------------
src/backend/utils/adt/pg_upgrade_support.c | 4 ++--
src/include/catalog/binary_upgrade.h | 4 ++--
3 files changed, 13 insertions(+), 18 deletions(-)
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 1b012c9cad..4ca2d3364b 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -85,8 +85,8 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
-Oid binary_upgrade_next_multirange_pg_type_oid = InvalidOid;
-Oid binary_upgrade_next_multirange_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
@@ -1529,13 +1529,9 @@ DefineRange(CreateRangeStmt *stmt)
/* alignment must be 'i' or 'd' for ranges */
alignment = (subtypalign == 'd') ? 'd' : 'i';
- /* Allocate OID for array type */
+ /* Allocate OID for array type, its multirange and its multirange array */
rangeArrayOid = AssignTypeArrayOid();
-
- /* Allocate OID for multirange type */
multirangeOid = AssignTypeMultirangeOid();
-
- /* Allocate OID for multirange array type */
multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
@@ -1574,7 +1570,6 @@ DefineRange(CreateRangeStmt *stmt)
Assert(typoid == address.objectId);
/* Create the multirange that goes with it */
-
multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
mltrngaddress =
@@ -2313,13 +2308,13 @@ AssignTypeMultirangeOid(void)
/* Use binary-upgrade override for pg_type.oid? */
if (IsBinaryUpgrade)
{
- if (!OidIsValid(binary_upgrade_next_multirange_pg_type_oid))
+ if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
- type_multirange_oid = binary_upgrade_next_multirange_pg_type_oid;
- binary_upgrade_next_multirange_pg_type_oid = InvalidOid;
+ type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
+ binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
}
else
{
@@ -2346,13 +2341,13 @@ AssignTypeMultirangeArrayOid(void)
/* Use binary-upgrade override for pg_type.oid? */
if (IsBinaryUpgrade)
{
- if (!OidIsValid(binary_upgrade_next_multirange_array_pg_type_oid))
+ if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
- type_multirange_array_oid = binary_upgrade_next_multirange_array_pg_type_oid;
- binary_upgrade_next_multirange_array_pg_type_oid = InvalidOid;
+ type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
+ binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
}
else
{
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index d980b96f48..418c26c81b 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -57,7 +57,7 @@ binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
Oid typoid = PG_GETARG_OID(0);
CHECK_IS_BINARY_UPGRADE;
- binary_upgrade_next_multirange_pg_type_oid = typoid;
+ binary_upgrade_next_mrng_pg_type_oid = typoid;
PG_RETURN_VOID();
}
@@ -68,7 +68,7 @@ binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
Oid typoid = PG_GETARG_OID(0);
CHECK_IS_BINARY_UPGRADE;
- binary_upgrade_next_multirange_array_pg_type_oid = typoid;
+ binary_upgrade_next_mrng_array_pg_type_oid = typoid;
PG_RETURN_VOID();
}
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 2b6e87bb84..ba132ddf23 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,8 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
-extern PGDLLIMPORT Oid binary_upgrade_next_multirange_pg_type_oid;
-extern PGDLLIMPORT Oid binary_upgrade_next_multirange_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
--
2.20.1
[text/x-diff] 0004-Protect-comment-against-pgindent.patch (1.2K, ../../20191130021026.GA19259@alvherre.pgsql/5-0004-Protect-comment-against-pgindent.patch)
download | inline diff:
From e78eb8bc8277bb09a98ae6b8c18252df834bce83 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 28 Nov 2019 19:36:53 -0300
Subject: [PATCH 4/6] Protect comment against pgindent
---
src/backend/utils/adt/multirangetypes.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 7717acb3b1..b8ff39077c 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -1136,15 +1136,16 @@ multirange_intersect_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *range
if (range_count1 == 0 || range_count2 == 0)
return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
- /*
+ /*-----------------------------------------------
* Worst case is a stitching pattern like this:
*
* mr1: --- --- --- ---
* mr2: --- --- ---
* mr3: - - - - - -
*
- * That seems to be range_count1 + range_count2 - 1, but one extra won't
- * hurt.
+ * That seems to be range_count1 + range_count2 - 1,
+ * but one extra won't hurt.
+ *-----------------------------------------------
*/
ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
range_count3 = 0;
--
2.20.1
[text/x-diff] 0005-pgindent.patch (3.5K, ../../20191130021026.GA19259@alvherre.pgsql/6-0005-pgindent.patch)
download | inline diff:
From 981a6862601e22141938b9cfdea9149b1fdf65f3 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 28 Nov 2019 19:35:43 -0300
Subject: [PATCH 5/6] pgindent
---
src/backend/utils/adt/multirangetypes.c | 14 +++++++-------
src/backend/utils/fmgr/funcapi.c | 12 ++++++------
src/bin/pg_dump/pg_dump.c | 11 ++++++-----
3 files changed, 19 insertions(+), 18 deletions(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index b8ff39077c..f4dec1bbc9 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -330,13 +330,13 @@ multirange_recv(PG_FUNCTION_ARGS)
Datum
multirange_send(PG_FUNCTION_ARGS)
{
- MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
- Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
- StringInfo buf = makeStringInfo();
- RangeType **ranges;
- int32 range_count;
- int32 i;
- MultirangeIOData *cache;
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 25623461bb..808a0e2ca7 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -630,8 +630,8 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
{
if (OidIsValid(anymultirange_type))
{
- Oid rngtype;
- Oid subtype;
+ Oid rngtype;
+ Oid subtype;
rngtype = resolve_generic_type(ANYRANGEOID,
anymultirange_type,
@@ -665,9 +665,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
{
if (OidIsValid(anyrange_type))
{
- Oid subtype;
- Oid mltrngtype;
- Oid rngtype;
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
subtype = resolve_generic_type(ANYELEMENTOID,
anyrange_type,
@@ -911,7 +911,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
{
Oid rngtype;
Oid subtype;
-
+
rngtype = resolve_generic_type(ANYRANGEOID,
anymultirange_type,
ANYMULTIRANGEOID);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 4a0a03d50c..8272a0451b 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -4276,15 +4276,16 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
-static Oid get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+static Oid
+get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
{
/*
* If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
+ * does, we must select an unused type OID to assign. This currently only
+ * happens for domains, when upgrading pre-v11 to v11 and up.
*
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
+ * Note: local state here is kind of ugly, but we must have some, since we
+ * mustn't choose the same unused OID more than once.
*/
static Oid next_possible_free_oid = FirstNormalObjectId;
PGresult *res;
--
2.20.1
[text/x-diff] 0006-Add-comment-and-asserts-to-makeUniqueTypeName.patch (1.5K, ../../20191130021026.GA19259@alvherre.pgsql/7-0006-Add-comment-and-asserts-to-makeUniqueTypeName.patch)
download | inline diff:
From 04f1d482757debaa7ad44e8b59d76dab33d95503 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 29 Nov 2019 23:03:13 -0300
Subject: [PATCH 6/6] Add comment and asserts to makeUniqueTypeName
---
src/backend/catalog/pg_type.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index de0bb24649..7b871e7b0d 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -912,8 +912,14 @@ makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
}
/*
- * makeUniqueTypeName: Prepend underscores as needed until we make a name that
- * doesn't collide with anything. Tries the original typeName if requested.
+ * makeUniqueTypeName
+ * Generate a unique name for a prospective new type
+ *
+ * Given a typeName of length namelen, produce a new name into dest (an output
+ * buffer allocated by caller, which must of length NAMEDATALEN) by prepending
+ * underscores, until a non-conflicting name results.
+ *
+ * If tryOriginalName, first try with zero underscores.
*
* Returns the number of underscores added.
*/
@@ -924,6 +930,9 @@ makeUniqueTypeName(char *dest, const char *typeName, size_t namelen, Oid typeNam
Relation pg_type_desc;
int i;
+ Assert(strlen(typeName) == namelen);
+ Assert(namelen < NAMEDATALEN);
+
pg_type_desc = table_open(TypeRelationId, AccessShareLock);
for (i = 0; i < NAMEDATALEN - 1; i++)
--
2.20.1
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-12-20 17:43 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-08 17:28 ` Re: range_agg David G. Johnston <david.g.johnston@gmail.com>
2 siblings, 3 replies; 152+ messages in thread
From: Alvaro Herrera @ 2019-12-20 17:43 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
I took the liberty of rebasing this series on top of recent branch
master. The first four are mostly Paul's originals, except for conflict
fixes; the rest are changes I'm proposing as I go along figuring out the
whole thing. (I would post just my proposed changes, if it weren't for
the rebasing; apologies for the messiness.)
I am not convinced that adding TYPTYPE_MULTIRANGE is really necessary.
Why can't we just treat those types as TYPTYPE_RANGE and distinguish
them using TYPCATEGORY_MULTIRANGE? That's what we do for arrays. I'll
try to do that next.
I think the algorithm for coming up with the multirange name is
suboptimal. It works fine with the name is short enough that we can add
a few extra letters, but otherwise the result look pretty silly. I
think we can still improve on that. I propose to make
makeUniqueTypeName accept a suffix, and truncate the letters that appear
*before* the suffix rather than truncating after it's been appended.
There's a number of ereport() calls that should become elog(); and a
bunch of others that should probably acquire errcode() and be
reformatted per our style.
Regarding Pavel's documentation markup issue,
> I am not sure how much is correct to use <literallayout class="monospaced">
> in doc. It is used for ranges, and multiranges, but no in other places
I looked at the generated PDF and the table looks pretty bad; the words
in those entries overlap the words in the cell to their right. But that
also happens with entries that do not use <literallayout class="x">!
See [1] for an example of the existing docs being badly formatted. The
docbook documentation [2] seems to suggest that what Paul used is the
appropriate way to do this.
Maybe a way is to make each entry have more than one row -- so the
example would appear below the other three fields in its own row, and
would be able to use the whole width of the table.
[1] https://twitter.com/alvherre/status/1205563468595781633
[2] https://tdg.docbook.org/tdg/5.1/literallayout.html
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
[text/x-diff] v7-0001-multirange-type-basics.patch (139.8K, ../../20191220174321.GA5956@alvherre.pgsql/2-v7-0001-multirange-type-basics.patch)
download | inline diff:
From e2dd9427d51c50898d9558c201f6d5df61e957a7 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:25:39 -0700
Subject: [PATCH v7 01/13] multirange type basics
---
src/backend/catalog/pg_proc.c | 16 +-
src/backend/catalog/pg_range.c | 11 +-
src/backend/catalog/pg_type.c | 124 ++-
src/backend/commands/typecmds.c | 171 +++-
src/backend/parser/parse_coerce.c | 303 +++++-
src/backend/utils/adt/Makefile | 1 +
src/backend/utils/adt/multirangetypes.c | 966 ++++++++++++++++++
src/backend/utils/adt/pseudotypes.c | 25 +
src/backend/utils/adt/rangetypes.c | 155 ++-
src/backend/utils/cache/lsyscache.c | 62 ++
src/backend/utils/cache/syscache.c | 11 +
src/backend/utils/cache/typcache.c | 109 +-
src/backend/utils/fmgr/funcapi.c | 271 ++++-
src/include/access/tupmacs.h | 4 +-
src/include/catalog/indexing.h | 3 +
src/include/catalog/pg_amop.dat | 22 +
src/include/catalog/pg_amproc.dat | 7 +
src/include/catalog/pg_opclass.dat | 4 +
src/include/catalog/pg_operator.dat | 33 +
src/include/catalog/pg_opfamily.dat | 4 +
src/include/catalog/pg_proc.dat | 77 ++
src/include/catalog/pg_range.dat | 15 +-
src/include/catalog/pg_range.h | 5 +-
src/include/catalog/pg_type.dat | 39 +
src/include/catalog/pg_type.h | 8 +-
src/include/utils/lsyscache.h | 3 +
src/include/utils/multirangetypes.h | 72 ++
src/include/utils/rangetypes.h | 2 +
src/include/utils/syscache.h | 1 +
src/include/utils/typcache.h | 6 +
src/pl/plpgsql/src/pl_comp.c | 5 +
src/test/regress/expected/hash_func.out | 13 +
src/test/regress/expected/multirangetypes.out | 292 ++++++
src/test/regress/expected/opr_sanity.out | 23 +-
src/test/regress/expected/type_sanity.out | 46 +-
src/test/regress/parallel_schedule | 3 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/hash_func.sql | 10 +
src/test/regress/sql/multirangetypes.sql | 65 ++
src/test/regress/sql/opr_sanity.sql | 18 +-
src/test/regress/sql/type_sanity.sql | 16 +-
41 files changed, 2890 insertions(+), 132 deletions(-)
create mode 100644 src/backend/utils/adt/multirangetypes.c
create mode 100644 src/include/utils/multirangetypes.h
create mode 100644 src/test/regress/expected/multirangetypes.out
create mode 100644 src/test/regress/sql/multirangetypes.sql
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index ef009ad2bc..80cc0721a5 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -192,6 +192,7 @@ ProcedureCreate(const char *procedureName,
genericInParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericInParam = true;
anyrangeInParam = true;
break;
@@ -219,6 +220,7 @@ ProcedureCreate(const char *procedureName,
genericOutParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericOutParam = true;
anyrangeOutParam = true;
break;
@@ -231,10 +233,10 @@ ProcedureCreate(const char *procedureName,
/*
* Do not allow polymorphic return type unless at least one input argument
- * is polymorphic. ANYRANGE return type is even stricter: must have an
- * ANYRANGE input (since we can't deduce the specific range type from
- * ANYELEMENT). Also, do not allow return type INTERNAL unless at least
- * one input argument is INTERNAL.
+ * is polymorphic. ANYRANGE and ANYMULTIRANGE return types are even
+ * stricter: must have an ANYRANGE or ANYMULTIRANGE input (since we can't
+ * deduce the specific range type from ANYELEMENT). Also, do not allow
+ * return type INTERNAL unless at least one input argument is INTERNAL.
*/
if ((IsPolymorphicType(returnType) || genericOutParam)
&& !genericInParam)
@@ -243,12 +245,12 @@ ProcedureCreate(const char *procedureName,
errmsg("cannot determine result data type"),
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
- if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
- !anyrangeInParam)
+ if ((returnType == ANYRANGEOID || returnType == ANYMULTIRANGEOID
+ || anyrangeOutParam) && !anyrangeInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
- errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
+ errdetail("A function returning \"anyrange\" or \"anymultirange\" must have at least one \"anyrange\" or \"anymultirange\" argument.")));
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
ereport(ERROR,
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index e6e138babd..d914f04858 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_mltrngtypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,13 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index d65a2d971f..8a85d3412a 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -36,6 +36,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static int makeUniqueTypeName(char *dest, const char *typeName, size_t namelen,
+ Oid typeNamespace, bool tryOriginalName);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -785,29 +788,10 @@ makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
char *arr = (char *) palloc(NAMEDATALEN);
int namelen = strlen(typeName);
- int i;
+ int underscores;
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
-
- if (i >= NAMEDATALEN - 1)
+ underscores = makeUniqueTypeName(arr, typeName, namelen, typeNamespace, false);
+ if (underscores >= NAMEDATALEN - 1)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -878,3 +862,99 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * the caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char mltrng[NAMEDATALEN];
+ char *mltrngunique = (char *) palloc(NAMEDATALEN);
+ int namelen = strlen(rangeTypeName);
+ int rangelen;
+ char *rangestr;
+ int rangeoffset;
+ int underscores;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ strlcpy(mltrng, rangeTypeName, NAMEDATALEN);
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ rangeoffset = rangestr - rangeTypeName;
+ rangelen = strlen(rangestr);
+ strlcpy(mltrng + rangeoffset, "multi", NAMEDATALEN - rangeoffset);
+ strlcpy(mltrng + rangeoffset + 5, rangestr, NAMEDATALEN - rangeoffset - 5);
+ namelen += 5;
+ }
+ else
+ {
+ strlcpy(mltrng + namelen, "multirange", NAMEDATALEN - namelen);
+ namelen += 10;
+ }
+
+ underscores = makeUniqueTypeName(mltrngunique, mltrng, namelen, typeNamespace, true);
+ if (underscores >= NAMEDATALEN - 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mltrngunique;
+}
+
+/*
+ * makeUniqueTypeName: Prepend underscores as needed until we make a name that
+ * doesn't collide with anything. Tries the original typeName if requested.
+ *
+ * Returns the number of underscores added.
+ */
+int
+makeUniqueTypeName(char *dest, const char *typeName, size_t namelen, Oid typeNamespace,
+ bool tryOriginalName)
+{
+ int i;
+
+ for (i = 0; i < NAMEDATALEN - 1; i++)
+ {
+ if (i == 0)
+ {
+ if (tryOriginalName &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ {
+ strcpy(dest, typeName);
+ break;
+ }
+
+ }
+ else
+ {
+ dest[i - 1] = '_';
+ if (i + namelen < NAMEDATALEN)
+ strcpy(dest + i, typeName);
+ else
+ {
+ strlcpy(dest + i, typeName, NAMEDATALEN);
+ truncate_identifier(dest, NAMEDATALEN, false);
+ }
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ break;
+ }
+ }
+
+ return i;
+}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 89887b8fd7..14a6857062 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -88,6 +88,8 @@ Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeArrayOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -811,7 +813,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1353,8 +1356,12 @@ DefineRange(CreateRangeStmt *stmt)
char *typeName;
Oid typeNamespace;
Oid typoid;
+ Oid mltrngtypoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1371,6 +1378,7 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1522,6 +1530,9 @@ DefineRange(CreateRangeStmt *stmt)
/* Allocate OID for array type */
rangeArrayOid = AssignTypeArrayOid();
+ /* Allocate OID for multirange array type */
+ multirangeArrayOid = AssignTypeArrayOid();
+
/* Create the pg_type entry */
address =
TypeCreate(InvalidOid, /* no predetermined type OID */
@@ -1557,9 +1568,47 @@ DefineRange(CreateRangeStmt *stmt)
InvalidOid); /* type's collation (ranges never have one) */
Assert(typoid == address.objectId);
+ /* Create the multirange that goes with it */
+
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(InvalidOid, /* no predetermined type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_MULTIRANGE, /* type-category (multirange type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ mltrngtypoid = mltrngaddress.objectId;
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, mltrngtypoid);
/*
* Create the array type that goes with it.
@@ -1600,8 +1649,49 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ mltrngtypoid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ mltrngtypoid, rangeArrayOid);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1679,6 +1769,83 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeArrayOid)
+{
+ static const char *const prosrc[2] = {"multirange_constructor0",
+ "multirange_constructor1"};
+ static const int pronargs[2] = {0, 1};
+
+ Oid constructorArgTypes[0];
+ ObjectAddress myself,
+ referenced;
+ int i;
+
+ constructorArgTypes[0] = rangeArrayOid;
+
+ Datum allParamTypes[1] = {ObjectIdGetDatum(rangeArrayOid)};
+ ArrayType *allParameterTypes = construct_array(allParamTypes, 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ Datum constructorAllParamTypes[2] = {PointerGetDatum(NULL), PointerGetDatum(allParameterTypes)};
+
+ Datum paramModes[1] = {CharGetDatum(FUNC_PARAM_VARIADIC)};
+ ArrayType *parameterModes = construct_array(paramModes, 1, CHAROID,
+ 1, true, 'c');
+ Datum constructorParamModes[2] = {PointerGetDatum(NULL), PointerGetDatum(parameterModes)};
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ for (i = 0; i < lengthof(prosrc); i++)
+ {
+ oidvector *constructorArgTypesVector;
+
+ constructorArgTypesVector = buildoidvector(constructorArgTypes,
+ pronargs[i]);
+
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace, /* namespace */
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR, /* language validator */
+ prosrc[i], /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ constructorArgTypesVector, /* parameterTypes */
+ constructorAllParamTypes[i], /* allParameterTypes */
+ constructorParamModes[i], /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+
+ /*
+ * Make the constructors internally-dependent on the multirange type
+ * so that they go away silently when the type is dropped. Note that
+ * pg_dump depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ }
+}
/*
* Find suitable I/O functions for a type.
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index a6c51a95da..4c7a5f7fbe 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -187,18 +187,20 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
- targetTypeId == ANYRANGEOID)
+ targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call anyarray_in,
- * anyenum_in, or anyrange_in, which will produce an error. Also, if
- * what we have is a domain over array, enum, or range, we have to
- * relabel it to its base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * anyarray_in, anyenum_in, anyrange_in, or anymultirange_in, which
+ * will produce an error. Also, if what we have is a domain over
+ * array, enum, range, or multirange, we have to relabel it to its
+ * base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1482,6 +1484,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid array_typelem;
Oid range_typeid = InvalidOid;
Oid range_typelem;
+ Oid multirange_typeid = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
@@ -1528,6 +1532,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1580,6 +1593,37 @@ check_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+ }
+
if (have_anynonarray)
{
/* require the element type to not be an array or domain over array */
@@ -1681,13 +1725,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid array_typelem;
- Oid range_typelem;
+ Oid range_typelem = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
+ bool have_anyrange = (rettype == ANYRANGEOID);
+ bool have_anymultirange = (rettype == ANYMULTIRANGEOID);
/*
* Loop through the arguments to see if we have any that are polymorphic.
@@ -1745,7 +1793,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else if (decl_type == ANYRANGEOID)
{
- have_generics = true;
+ have_generics = have_anyrange = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
@@ -1763,6 +1811,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ have_generics = have_anymultirange = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
}
/*
@@ -1813,9 +1881,12 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- if (range_typeid == ANYRANGEOID && !have_anyelement)
+ if (range_typeid == ANYRANGEOID && !have_anyelement && !have_anymultirange)
{
- /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
+ /*
+ * Special case for ANYRANGE input: okay iff no ANYELEMENT or
+ * ANYMULTIRANGE
+ */
range_typelem = ANYELEMENTOID;
}
else
@@ -1849,6 +1920,69 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Get the range type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ if (multirange_typeid == ANYMULTIRANGEOID && !have_anyrange && !have_anyelement)
+ {
+ /*
+ * Special case for ANYMULTIRANGE input: okay iff no ANYRANGE or
+ * ANYELEMENT
+ */
+ multirange_typelem = ANYRANGEOID;
+ }
+ else
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+ }
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ /*
+ * Infer the element type too if needed (if there is an ANYMULTIRANGE
+ * and ANYELEMENT, with no ANYRANGE).
+ */
+ if (!OidIsValid(elem_typeid))
+ {
+ elem_typeid = get_range_subtype(range_typeid);
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
+ }
+
if (!OidIsValid(elem_typeid))
{
if (allow_poly)
@@ -1856,6 +1990,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -1928,6 +2063,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -1959,6 +2105,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
@@ -2007,8 +2169,7 @@ resolve_generic_type(Oid declared_type,
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
- context_declared_type == ANYENUMOID ||
- context_declared_type == ANYRANGEOID)
+ context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
@@ -2020,11 +2181,37 @@ resolve_generic_type(Oid declared_type,
format_type_be(context_actual_type))));
return array_typeid;
}
+ else if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid range_typelem = get_range_subtype(context_base_type);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+ Oid range_typelem = get_range_subtype(multirange_typelem);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
- declared_type == ANYENUMOID ||
- declared_type == ANYRANGEOID)
+ declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
@@ -2052,6 +2239,19 @@ resolve_generic_type(Oid declared_type,
"anyrange", format_type_be(context_base_type))));
return range_typelem;
}
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ /* Use the element type corresponding to actual type */
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
@@ -2060,6 +2260,74 @@ resolve_generic_type(Oid declared_type,
return context_actual_type;
}
}
+ else if (declared_type == ANYRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
+ else
+ {
+ /* We can't infer a range from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
+ else if (declared_type == ANYMULTIRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typeid = get_range_multirange(context_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return multirange_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else
+ {
+ /* We can't infer a multirange from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
else
{
/* declared_type isn't polymorphic, so return it as-is */
@@ -2174,6 +2442,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..7118dd0034 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..5323a6a635
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,966 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/hashutils.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ Oid typiofunc; /* range type's I/O function */
+ Oid typioparam; /* range type's I/O parameter */
+ FmgrInfo proc; /* lookup result for typiofunc */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData * get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks either either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ Oid rngtypoid;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+ rngtypoid = rangetyp->type_id;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */ ;
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **) repalloc(ranges,
+ range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(
+ InputFunctionCall(&cache->proc, range_str_copy,
+ cache->typioparam, typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "Unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->proc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(
+ ReceiveFunctionCall(&cache->proc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range = RangeTypePGetDatum(ranges[i]);
+ range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
+ uint32 range_len = VARSIZE(range) - VARHDRSZ;
+ char *range_data = VARDATA(range);
+
+ pq_sendint32(buf, range_len);
+ pq_sendbytes(buf, range_data, range_len);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &cache->typiofunc);
+
+ if (!OidIsValid(cache->typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(cache->typiofunc, &cache->proc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ RangeType *range;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ bytelen += MAXALIGN(VARSIZE(range));
+ }
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ memcpy(ptr, range, VARSIZE(range));
+ ptr += MAXALIGN(VARSIZE(range));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of any ranges you like into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ * Returns the number of slots actually used,
+ * which may be less than input_range_count but never more.
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType * multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR, (errmsg("Can't construct multirange with a multi-dimensional array")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL element")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ ereport(ERROR, (errmsg("Zero-param multirange constructor shouldn't have arguments")));
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 5c886cfe96..94484b3373 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -184,6 +185,30 @@ anyrange_out(PG_FUNCTION_ARGS)
return range_out(fcinfo);
}
+/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
/*
* void_in - input routine for pseudo-type VOID.
*
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 461c428413..65f68614c1 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -1177,12 +1175,68 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
+ return cmp;
+}
+
+/* btree comparator */
+Datum
+range_cmp(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int cmp;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ cmp = range_cmp_internal(typcache, r1, r2);
+
PG_FREE_IF_COPY(r1, 0);
PG_FREE_IF_COPY(r2, 1);
PG_RETURN_INT32(cmp);
}
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ /* For b-tree use, empty ranges sort before all else */
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
/* inequality operators using the range_cmp function */
Datum
range_lt(PG_FUNCTION_ARGS)
@@ -1218,13 +1272,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1284,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1325,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1354,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1392,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1937,6 +2005,45 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
b1->val, b2->val));
}
+/*
+ * Compares two ranges so we can qsort them.
+ * This expects that you give qsort a RangeType **,
+ * so the RangeTypes can be in diverse locations,
+ * as long as you have a list of pointers to them all.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
/*
* Build an empty range value of the type indicated by the typcache entry.
*/
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 27602fa49c..360a71427e 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2468,6 +2468,16 @@ type_is_range(Oid typid)
return (get_typtype(typid) == TYPTYPE_RANGE);
}
+/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
/*
* get_type_category_preferred
*
@@ -3150,6 +3160,58 @@ get_range_subtype(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->mltrngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/* ---------- PG_MULTIRANGE CACHE ---------- */
+
+/*
+ * get_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_multirange_subtype(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index d69c0ff813..6156ab72a1 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -508,6 +508,17 @@ static const struct cachedesc cacheinfo[] = {
},
4
},
+ {RangeRelationId, /* MULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_mltrngtypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{NamespaceRelationId, /* NAMESPACENAME */
NamespaceNameIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 11920db0d9..0630687b49 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -278,6 +278,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -294,6 +295,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -500,8 +504,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -538,7 +542,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -563,7 +567,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -588,7 +592,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -640,6 +644,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -684,6 +695,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -758,6 +776,16 @@ lookup_type_cache(Oid type_id, int flags)
load_rangetype_info(typentry);
}
+ /*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
/*
* If requested, get information about a domain type
*/
@@ -870,6 +898,33 @@ load_rangetype_info(TypeCacheEntry *typentry)
}
+/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Form_pg_range pg_range;
+ HeapTuple tup;
+ Oid rangetypeOid;
+
+ /* get information from pg_range */
+ tup = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(typentry->type_id));
+ /* should not fail, since we already checked typtype ... */
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+ pg_range = (Form_pg_range) GETSTRUCT(tup);
+
+ rangetypeOid = pg_range->rngtypid;
+
+ ReleaseSysCache(tup);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
+
+
/*
* load_domaintype_info --- helper routine to set up domain constraint info
*
@@ -1469,11 +1524,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1516,6 +1571,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 4688fbc50c..d65968d4eb 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -470,11 +470,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anynonarray = false;
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
Oid anycollation = InvalidOid;
int i;
@@ -500,12 +502,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
case ANYRANGEOID:
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_anymultirange_result = true;
+ break;
default:
break;
}
}
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/*
@@ -533,6 +538,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (!OidIsValid(anyrange_type))
anyrange_type = get_call_expr_argtype(call_expr, i);
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(anymultirange_type))
+ anymultirange_type = get_call_expr_argtype(call_expr, i);
+ break;
default:
break;
}
@@ -540,7 +549,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/* If nothing found, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -561,19 +570,122 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ Oid rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ anymultirange_type = mltrngtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
/* Enforce ANYNONARRAY if needed */
if (have_anynonarray && type_is_array(anyelement_type))
@@ -640,6 +752,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
default:
break;
}
@@ -664,9 +784,11 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
int inargno;
int i;
@@ -725,6 +847,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ have_anymultirange_result = true;
+ else
+ {
+ if (!OidIsValid(anymultirange_type))
+ {
+ anymultirange_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anymultirange_type))
+ return false;
+ }
+ argtypes[i] = anymultirange_type;
+ }
+ break;
default:
break;
}
@@ -734,12 +871,12 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
/* Done? */
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/* If no input polymorphics, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -760,19 +897,122 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ Oid rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ }
+ else
+ {
+ return false;
+ }
+ }
/* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */
@@ -792,6 +1032,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = anymultirange_type;
+ break;
default:
break;
}
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 23cf481e78..50b4c4428f 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -137,8 +137,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ef4445b017..ca9890ab6c 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -330,6 +330,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_mltrngtypid_index, 8001, on pg_range using btree(mltrngtypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 232557ee81..999493f94d 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 5e705019b4..b7cb2cb000 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -225,6 +225,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
@@ -385,6 +387,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index 2d575102ef..2e59cf9159 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 126bfac3ff..7b7ce38076 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3297,5 +3297,38 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'contsel', oprjoin => 'contjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 41e40d657a..536c10cb0c 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index ac8f64b219..abb956514e 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9722,6 +9722,83 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor1' },
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index dd9baa2678..9fd781e7b5 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ mltrngtypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', mltrngtypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', mltrngtypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', mltrngtypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ mltrngtypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ mltrngtypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index b01a074d09..9cfc56d4d2 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -45,6 +45,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* subtype difference as a float8, or 0 */
regproc rngsubdiff BKI_LOOKUP(pg_proc);
+
+ /* OID of the range's multirange type */
+ Oid mltrngtypid BKI_LOOKUP(pg_type);
} FormData_pg_range;
/* ----------------
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index d9b35af914..9a84d029d1 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -486,6 +486,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -594,5 +628,10 @@
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 0c273a0449..4f71a7f451 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -258,6 +258,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -269,6 +270,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPCATEGORY_ENUM 'E'
#define TYPCATEGORY_GEOMETRIC 'G'
#define TYPCATEGORY_NETWORK 'I' /* think INET */
+#define TYPCATEGORY_MULTIRANGE 'M'
#define TYPCATEGORY_NUMERIC 'N'
#define TYPCATEGORY_PSEUDOTYPE 'P'
#define TYPCATEGORY_RANGE 'R'
@@ -284,7 +286,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#endif /* EXPOSE_TO_CLIENT_CODE */
@@ -343,4 +346,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index c8df5bff9f..ffdd90d69c 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -154,6 +154,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -179,6 +180,8 @@ extern void free_attstatsslot(AttStatsSlot *sslot);
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..ed2e19aafa
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,72 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ char vl_len_[4]; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType * range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType * make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType * make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 11ed19ccfd..521710d71f 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 918765cc99..32603eeb58 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -66,6 +66,7 @@ enum SysCacheIdentifier
INDEXRELID,
LANGNAME,
LANGOID,
+ MULTIRANGETYPE,
NAMESPACENAME,
NAMESPACEOID,
OPERNAMENSP,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index 04bf28180d..eda39881ef 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -98,6 +98,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_canonical_finfo; /* canonicalization function, if any */
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
+ /*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype;
+
/*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
@@ -141,6 +146,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 3fb8fc7bad..f575f882a2 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -511,6 +511,8 @@ do_compile(FunctionCallInfo fcinfo,
rettypeid = INT4ARRAYOID;
else if (rettypeid == ANYRANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2494,6 +2496,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYRANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..5749537635
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,292 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index c19740e5db..916e1016b5 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -206,6 +213,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -224,6 +233,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -329,13 +340,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
@@ -343,16 +355,19 @@ ORDER BY 2;
2502 | anyarray_recv
2312 | anyelement_in
3504 | anyenum_in
+ 8002 | anymultirange_in
2777 | anynonarray_in
3832 | anyrange_in
750 | array_in
2400 | array_recv
3506 | enum_in
3532 | enum_recv
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(13 rows)
+(16 rows)
-- Look for functions that accept cstring and are neither datatype input
-- functions nor encoding conversion functions. It's almost never a good
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index cd7fc03b04..8ef9595c37 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -193,18 +193,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -238,17 +239,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -316,18 +318,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -361,17 +364,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -629,3 +633,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.mltrngtypid
+FROM pg_range p1
+WHERE p1.mltrngtypid IS NULL OR p1.mltrngtypid = 0;
+ rngtypid | rngsubtype | mltrngtypid
+----------+------------+-------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index d33a4e143d..5fc0673efa 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index f86f5c5682..98a9afbc7d 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..8651ba3f3d
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,65 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 624bea46ce..23932cfef0 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -273,13 +284,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- Look for functions that accept cstring and are neither datatype input
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fed413bf98..b102c01bbc 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -151,7 +151,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -180,7 +180,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -232,7 +232,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -261,7 +261,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -465,3 +465,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.mltrngtypid
+FROM pg_range p1
+WHERE p1.mltrngtypid IS NULL OR p1.mltrngtypid = 0;
--
2.20.1
[text/x-diff] v7-0002-multirange-operators-and-functions.patch (173.0K, ../../20191220174321.GA5956@alvherre.pgsql/3-v7-0002-multirange-operators-and-functions.patch)
download | inline diff:
From 57d3a34cc7415ef878d1d613836e7452527b0a2d Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:20 -0700
Subject: [PATCH v7 02/13] multirange operators and functions
---
src/backend/catalog/pg_type.c | 2 -
src/backend/commands/typecmds.c | 6 +-
src/backend/parser/parse_coerce.c | 2 +
src/backend/utils/adt/multirangetypes.c | 1389 +++++++++-
src/backend/utils/adt/rangetypes.c | 236 +-
src/backend/utils/fmgr/funcapi.c | 97 +-
src/include/catalog/pg_aggregate.dat | 11 +
src/include/catalog/pg_amproc.dat | 5 +-
src/include/catalog/pg_operator.dat | 169 ++
src/include/catalog/pg_proc.dat | 185 ++
src/include/utils/multirangetypes.h | 34 +
src/include/utils/rangetypes.h | 31 +-
src/test/regress/expected/dependency.out | 1 +
src/test/regress/expected/multirangetypes.out | 2397 +++++++++++++++++
src/test/regress/expected/opr_sanity.out | 4 +-
src/test/regress/expected/rangetypes.out | 38 +-
src/test/regress/expected/sanity_check.out | 2 +
src/test/regress/sql/multirangetypes.sql | 607 +++++
src/test/regress/sql/rangetypes.sql | 13 +
19 files changed, 5095 insertions(+), 134 deletions(-)
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 8a85d3412a..73b8ace4c5 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -876,7 +876,6 @@ makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
char mltrng[NAMEDATALEN];
char *mltrngunique = (char *) palloc(NAMEDATALEN);
int namelen = strlen(rangeTypeName);
- int rangelen;
char *rangestr;
int rangeoffset;
int underscores;
@@ -892,7 +891,6 @@ makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
if (rangestr)
{
rangeoffset = rangestr - rangeTypeName;
- rangelen = strlen(rangestr);
strlcpy(mltrng + rangeoffset, "multi", NAMEDATALEN - rangeoffset);
strlcpy(mltrng + rangeoffset + 5, rangestr, NAMEDATALEN - rangeoffset - 5);
namelen += 5;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 14a6857062..7a440cafd1 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1783,13 +1783,11 @@ makeMultirangeConstructors(const char *name, Oid namespace,
"multirange_constructor1"};
static const int pronargs[2] = {0, 1};
- Oid constructorArgTypes[0];
+ Oid constructorArgTypes = rangeArrayOid;
ObjectAddress myself,
referenced;
int i;
- constructorArgTypes[0] = rangeArrayOid;
-
Datum allParamTypes[1] = {ObjectIdGetDatum(rangeArrayOid)};
ArrayType *allParameterTypes = construct_array(allParamTypes, 1, OIDOID,
sizeof(Oid), true, 'i');
@@ -1808,7 +1806,7 @@ makeMultirangeConstructors(const char *name, Oid namespace,
{
oidvector *constructorArgTypesVector;
- constructorArgTypesVector = buildoidvector(constructorArgTypes,
+ constructorArgTypesVector = buildoidvector(&constructorArgTypes,
pronargs[i]);
myself = ProcedureCreate(name, /* name: same as multirange type */
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 4c7a5f7fbe..49c75dc12d 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1918,6 +1918,8 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(range_typeid),
format_type_be(elem_typeid))));
}
+ else
+ range_typelem = InvalidOid;
}
/* Get the range type based on the multirange type, if we have one */
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 5323a6a635..7717acb3b1 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -91,7 +91,6 @@ multirange_in(PG_FUNCTION_ARGS)
Oid mltrngtypoid = PG_GETARG_OID(1);
Oid typmod = PG_GETARG_INT32(2);
TypeCacheEntry *rangetyp;
- Oid rngtypoid;
int32 ranges_seen = 0;
int32 range_count = 0;
int32 range_capacity = 8;
@@ -101,13 +100,12 @@ multirange_in(PG_FUNCTION_ARGS)
MultirangeType *ret;
MultirangeParseState parse_state;
const char *ptr = input_str;
- const char *range_str;
+ const char *range_str = NULL;
int32 range_str_len;
char *range_str_copy;
cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
rangetyp = cache->typcache->rngtype;
- rngtypoid = rangetyp->type_id;
/* consume whitespace */
while (*ptr != '\0' && isspace((unsigned char) *ptr))
@@ -351,9 +349,12 @@ multirange_send(PG_FUNCTION_ARGS)
for (i = 0; i < range_count; i++)
{
Datum range = RangeTypePGetDatum(ranges[i]);
+ uint32 range_len;
+ char *range_data;
+
range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
- uint32 range_len = VARSIZE(range) - VARHDRSZ;
- char *range_data = VARDATA(range);
+ range_len = VARSIZE(range) - VARHDRSZ;
+ range_data = VARDATA(range);
pq_sendint32(buf, range_len);
pq_sendbytes(buf, range_data, range_len);
@@ -726,6 +727,829 @@ multirange_constructor0(PG_FUNCTION_ARGS)
}
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+range_union_range(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ MultirangeType *mr = make_multirange(mltrngtypoid, typcache->rngtype, 1, &r2);
+
+ PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r1, mr));
+}
+
+Datum
+range_union_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_union_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_MULTIRANGE_P(range_union_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_union_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
+}
+
+MultirangeType *
+range_union_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (RangeIsEmpty(r))
+ return mr;
+ if (MultirangeIsEmpty(mr))
+ return make_multirange(typcache->type_id, typcache->rngtype, 1, &r);
+
+ multirange_deserialize(mr, &range_count, &ranges1);
+
+ ranges2 = palloc0((range_count + 1) * sizeof(RangeType *));
+
+ memcpy(ranges2, ranges1, range_count * sizeof(RangeType *));
+ ranges2[range_count] = r;
+ return make_multirange(typcache->type_id, typcache->rngtype, range_count + 1, ranges2);
+}
+
+/* multirange minus */
+Datum
+range_minus_range(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r1) || RangeIsEmpty(r2))
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, rangetyp, 1, &r1));
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r1,
+ 1,
+ &r2));
+}
+
+Datum
+range_minus_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, rangetyp, 1, &r));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r,
+ range_count,
+ ranges));
+}
+
+Datum
+multirange_minus_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr) || RangeIsEmpty(r))
+ PG_RETURN_MULTIRANGE_P(mr);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count,
+ ranges,
+ 1,
+ &r));
+}
+
+Datum
+multirange_minus_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1, int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+range_intersect_range(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r1) || RangeIsEmpty(r2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r1,
+ 1,
+ &r2));
+}
+
+Datum
+range_intersect_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr2);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count2;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (RangeIsEmpty(r1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ 1,
+ &r1,
+ range_count2,
+ ranges2));
+}
+
+Datum
+multirange_intersect_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r2 = PG_GETARG_RANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ RangeType **ranges1;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || RangeIsEmpty(r2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ 1,
+ &r2));
+}
+
+Datum
+multirange_intersect_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_multirange_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+ /*
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1, but one extra won't
+ * hurt.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_multirange_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
/* multirange, multirange -> bool functions */
/* equality (internal version) */
@@ -795,6 +1619,538 @@ multirange_ne(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
}
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType * mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges1[0], ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1, MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
+ return false;
+
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
+ return false;
+ }
+
+ /* All ranges in mr2 are satisfied */
+ return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
/* Btree support */
/* btree comparator */
@@ -891,6 +2247,29 @@ multirange_gt(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(cmp > 0);
}
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
/* Hash support */
/* hash a multirange value */
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 65f68614c1..0651618363 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -429,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -450,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -473,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -483,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -493,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -503,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -513,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -955,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -967,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -991,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1099,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1108,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1130,54 +1180,83 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
}
-/* Btree support */
+/* range, range -> range, range functions */
-/* btree comparator */
-Datum
-range_cmp(PG_FUNCTION_ARGS)
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
{
- RangeType *r1 = PG_GETARG_RANGE_P(0);
- RangeType *r2 = PG_GETARG_RANGE_P(1);
- TypeCacheEntry *typcache;
RangeBound lower1,
lower2;
RangeBound upper1,
upper2;
bool empty1,
empty2;
- int cmp;
-
- check_stack_depth(); /* recurses when subtype is a range type */
-
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- /* For b-tree use, empty ranges sort before all else */
- if (empty1 && empty2)
- cmp = 0;
- else if (empty1)
- cmp = -1;
- else if (empty2)
- cmp = 1;
- else
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
{
- cmp = range_cmp_bounds(typcache, &lower1, &lower2);
- if (cmp == 0)
- cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
}
- return cmp;
+ return false;
}
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* Btree support */
+
/* btree comparator */
Datum
range_cmp(PG_FUNCTION_ARGS)
@@ -1207,7 +1286,7 @@ range_cmp(PG_FUNCTION_ARGS)
* Internal version of range_cmp
*/
int
-range_cmp_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
{
RangeBound lower1,
lower2;
@@ -1273,7 +1352,7 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
uint32
-hash_range_internal(TypeCacheEntry *typcache, RangeType *r)
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
uint32 result;
TypeCacheEntry *scache;
@@ -1343,7 +1422,7 @@ hash_range(PG_FUNCTION_ARGS)
}
uint64
-hash_range_extended_internal(TypeCacheEntry *typcache, RangeType *r, Datum seed)
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
{
uint64 result;
TypeCacheEntry *scache;
@@ -1837,6 +1916,21 @@ range_get_flags(const RangeType *range)
return *((char *) range + VARSIZE(range) - 1);
}
+/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index d65968d4eb..25623461bb 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -573,18 +573,20 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
anyrange_type = rngtype;
-
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -628,18 +630,21 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
{
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
anyrange_type = rngtype;
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -660,21 +665,25 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
{
if (OidIsValid(anyrange_type))
{
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and range results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
return false;
anyelement_type = subtype;
- Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
- anyrange_type,
- ANYRANGEOID);
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent range and multirange results */
- Oid rngtype = get_multirange_subtype(mltrngtype);
+ rngtype = get_multirange_subtype(mltrngtype);
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
@@ -900,18 +909,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
anyrange_type = rngtype;
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -955,18 +967,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
{
if (OidIsValid(anymultirange_type))
{
- Oid rngtype = resolve_generic_type(ANYRANGEOID,
- anymultirange_type,
- ANYMULTIRANGEOID);
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
/* check for inconsistent range and multirange results */
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
anyrange_type = rngtype;
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and multirange results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
@@ -987,21 +1002,25 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
{
if (OidIsValid(anyrange_type))
{
- Oid subtype = resolve_generic_type(ANYELEMENTOID,
- anyrange_type,
- ANYRANGEOID);
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent array and range results */
if (OidIsValid(anyelement_type) && anyelement_type != subtype)
return false;
anyelement_type = subtype;
- Oid mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
- anyrange_type,
- ANYRANGEOID);
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
/* check for inconsistent range and multirange results */
- Oid rngtype = get_multirange_subtype(mltrngtype);
+ rngtype = get_multirange_subtype(mltrngtype);
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index 242d843347..07a7211c15 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index b7cb2cb000..32965d6b3d 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -1210,12 +1210,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 7b7ce38076..1afdc1db7a 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3330,5 +3330,174 @@
oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8138', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anyrange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@+(anyrange,anyrange)',
+ oprcode => 'range_union_range' },
+{ oid => '8115', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@+(anymultirange,anyrange)',
+ oprcode => 'range_union_multirange' },
+{ oid => '8116', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@+(anyrange,anymultirange)',
+ oprcode => 'multirange_union_range' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '@+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union_multirange' },
+{ oid => '8140', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anyrange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcode => 'range_minus_range' },
+{ oid => '8121', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'range_minus_multirange' },
+{ oid => '8122', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus_range' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '@-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus_multirange' },
+{ oid => '8142', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anyrange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@*(anyrange,anyrange)',
+ oprcode => 'range_intersect_range' },
+{ oid => '8127', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@*(anymultirange,anyrange)',
+ oprcode => 'range_intersect_multirange' },
+{ oid => '8128', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'anymultirange', oprcom => '@*(anyrange,anymultirange)',
+ oprcode => 'multirange_intersect_range' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '@*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '@*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect_multirange' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index abb956514e..4311db01ff 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9605,6 +9605,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9654,6 +9658,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9741,9 +9752,165 @@
{ oid => '8007', descr => 'I/O',
proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
{ oid => '8008', descr => 'multirange typanalyze',
proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8137',
+ proname => 'range_union_range', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_union_range' },
+{ oid => '8112',
+ proname => 'range_union_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_union_multirange' },
+{ oid => '8113',
+ proname => 'multirange_union_range', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_union_range' },
+{ oid => '8114',
+ proname => 'multirange_union_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union_multirange' },
+{ oid => '8139',
+ proname => 'range_minus_range', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_minus_range' },
+{ oid => '8118',
+ proname => 'range_minus_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_minus_multirange' },
+{ oid => '8119',
+ proname => 'multirange_minus_range', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_minus_range' },
+{ oid => '8120',
+ proname => 'multirange_minus_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus_multirange' },
+{ oid => '8141',
+ proname => 'range_intersect_range', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_range' },
+{ oid => '8124',
+ proname => 'range_intersect_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_intersect_multirange' },
+{ oid => '8125',
+ proname => 'multirange_intersect_range', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_intersect_range' },
+{ oid => '8126',
+ proname => 'multirange_intersect_multirange', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_multirange' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
{ oid => '8045', descr => 'int4multirange constructor',
proname => 'int4multirange', proisstrict => 'f',
@@ -9799,6 +9966,24 @@
prorettype => 'int8multirange', proargtypes => '_int8range',
proallargtypes => '{_int8range}', proargmodes => '{v}',
prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
index ed2e19aafa..cc4f82b0ba 100644
--- a/src/include/utils/multirangetypes.h
+++ b/src/include/utils/multirangetypes.h
@@ -59,6 +59,40 @@ extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr
MultirangeType * mr2);
extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
MultirangeType * mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern MultirangeType * range_union_multirange_internal(TypeCacheEntry *typcache,
+ RangeType *r,
+ MultirangeType * mr);
+extern MultirangeType * multirange_minus_multirange_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType * multirange_intersect_multirange_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
/* assorted support functions */
extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 521710d71f..0bee724886 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -94,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -117,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -127,15 +141,20 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
- const RangeBound *b2);
+ const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
- const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+ const RangeBound *b2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..778699a961 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -141,6 +141,7 @@ owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
owner of type deptest_range
+owner of type deptest_multirange
owner of table deptest2
owner of sequence ss1
owner of type deptest_t
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
index 5749537635..2276d6d002 100644
--- a/src/test/regress/expected/multirangetypes.out
+++ b/src/test/regress/expected/multirangetypes.out
@@ -290,3 +290,2400 @@ select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
{[a,d)}
(1 row)
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT 'empty'::numrange @+ 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @+ nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @+ 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @+ nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @+ numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT 'empty'::numrange @+ nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @+ numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @+ nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(2,4);
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT numrange(1,2) @+ numrange(3,4);
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT 'empty'::numrange @- 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @- nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT 'empty'::numrange @- numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @- 'empty'::numrange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange() @- numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(1,3);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(1,2);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(2,4);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- numrange(3,4);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(1,3));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @- nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,10) @- nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------------
+ {[2,4),[5,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(0,2);
+ ?column?
+----------
+ {[2,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(1,2);
+ ?column?
+----------
+ {[2,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(3,4);
+ ?column?
+----------------
+ {[1,3),[4,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,10)) @- numrange(13,18);
+ ?column?
+----------
+ {[1,10)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) @- nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) @- nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT 'empty'::numrange @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT 'empty'::numrange @* nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,2) @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @* 'empty'::numrange;
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* numrange(1,2);
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() @* nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) @* nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,3);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(1,5);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,3) @* numrange(2,5);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,5) @* numrange(2,3);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,3);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,2);
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(1,5);
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* numrange(2,5);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @* numrange(2,3);
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(1,3) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(1,2) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT numrange(1,5) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT numrange(2,5) @* '{[1,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT numrange(2,3) @* '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange @* '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange @* '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange @* '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select $1 @+ $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 916e1016b5..e818eaea79 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1101,13 +1101,15 @@ ORDER BY 1, 2;
?| | ?|
?|| | ?||
@ | ~
+ @* | @*
+ @+ | @+
@@ | @@
@@@ | @@@
| | |
~<=~ | ~>=~
~<~ | ~>~
~= | ~=
-(30 rows)
+(32 rows)
-- Likewise for negator pairs.
SELECT DISTINCT o1.oprname AS op1, o2.oprname AS op2
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 6fd16bddd1..1bfe426be6 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1284,7 +1302,7 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
@@ -1392,6 +1410,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1400,6 +1419,16 @@ select * from outparam_succeed(int4range(1,2));
[1,2) | foo
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1408,6 +1437,7 @@ select * from inoutparam_succeed(int4range(1,2));
2 | [1,2)
(1 row)
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
select * from table_succeed(123, int4range(1,11));
@@ -1420,14 +1450,14 @@ select * from table_succeed(123, int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 070de78e85..6f7fcf6326 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
onek|t
onek2|t
path_tbl|f
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
index 8651ba3f3d..b3ede18f96 100644
--- a/src/test/regress/sql/multirangetypes.sql
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -63,3 +63,610 @@ select textmultirange();
select textmultirange(textrange('a', 'c'));
select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT 'empty'::numrange @+ 'empty'::numrange;
+SELECT 'empty'::numrange @+ nummultirange();
+SELECT nummultirange() @+ 'empty'::numrange;
+SELECT nummultirange() @+ nummultirange();
+SELECT 'empty'::numrange @+ numrange(1,2);
+SELECT 'empty'::numrange @+ nummultirange(numrange(1,2));
+SELECT numrange(1,2) @+ nummultirange();
+SELECT nummultirange(numrange(1,2)) @+ 'empty'::numrange;
+SELECT numrange(1,2) @+ 'empty'::numrange;
+SELECT nummultirange() @+ numrange(1,2);
+SELECT nummultirange() @+ nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange();
+SELECT numrange(1,2) @+ numrange(1,2);
+SELECT numrange(1,2) @+ numrange(2,4);
+SELECT numrange(1,2) @+ numrange(3,4);
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) @+ nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @+ nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT 'empty'::numrange @- 'empty'::numrange;
+SELECT 'empty'::numrange @- nummultirange();
+SELECT nummultirange() @- 'empty'::numrange;
+SELECT nummultirange() @- nummultirange();
+SELECT 'empty'::numrange @- numrange(1,2);
+SELECT 'empty'::numrange @- nummultirange(numrange(1,2));
+SELECT numrange(1,2) @- nummultirange();
+SELECT nummultirange(numrange(1,2)) @- 'empty'::numrange;
+SELECT 'empty'::numrange @- numrange(1,2);
+SELECT numrange(1,2) @- 'empty'::numrange;
+SELECT nummultirange() @- numrange(1,2);
+SELECT nummultirange() @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @- nummultirange();
+SELECT numrange(1,3) @- numrange(1,3);
+SELECT numrange(1,3) @- numrange(1,2);
+SELECT numrange(1,3) @- numrange(2,4);
+SELECT numrange(1,3) @- numrange(3,4);
+SELECT numrange(1,3) @- nummultirange(numrange(1,3));
+SELECT numrange(1,3) @- nummultirange(numrange(1,2));
+SELECT numrange(1,3) @- nummultirange(numrange(2,4));
+SELECT numrange(1,3) @- nummultirange(numrange(3,4));
+SELECT numrange(1,10) @- nummultirange(numrange(1,2), numrange(4,5));
+SELECT nummultirange(numrange(1,10)) @- numrange(0,2);
+SELECT nummultirange(numrange(1,10)) @- numrange(1,2);
+SELECT nummultirange(numrange(1,10)) @- numrange(3,4);
+SELECT nummultirange(numrange(1,10)) @- numrange(13,18);
+SELECT nummultirange(numrange(1,2), numrange(3,4)) @- nummultirange();
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) @- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) @- nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) @- nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) @- nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) @- nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT 'empty'::numrange @* 'empty'::numrange;
+SELECT 'empty'::numrange @* nummultirange();
+SELECT nummultirange() @* 'empty'::numrange;
+SELECT nummultirange() @* nummultirange();
+SELECT 'empty'::numrange @* numrange(1,2);
+SELECT 'empty'::numrange @* nummultirange(numrange(1,2));
+SELECT numrange(1,2) @* 'empty'::numrange;
+SELECT numrange(1,2) @* nummultirange();
+SELECT nummultirange(numrange(1,2)) @* 'empty'::numrange;
+SELECT nummultirange() @* numrange(1,2);
+SELECT nummultirange() @* nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) @* nummultirange();
+SELECT numrange(1,3) @* numrange(1,3);
+SELECT numrange(1,3) @* numrange(1,2);
+SELECT numrange(1,3) @* numrange(1,5);
+SELECT numrange(1,3) @* numrange(2,5);
+SELECT numrange(1,5) @* numrange(2,3);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,3);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,2);
+SELECT '{[1,3)}'::nummultirange @* numrange(1,5);
+SELECT '{[1,3)}'::nummultirange @* numrange(2,5);
+SELECT '{[1,5)}'::nummultirange @* numrange(2,3);
+SELECT numrange(1,3) @* '{[1,3)}'::nummultirange;
+SELECT numrange(1,2) @* '{[1,3)}'::nummultirange;
+SELECT numrange(1,5) @* '{[1,3)}'::nummultirange;
+SELECT numrange(2,5) @* '{[1,3)}'::nummultirange;
+SELECT numrange(2,3) @* '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange @* '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange @* '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange @* '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange @* '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select $1 @+ $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 8960add976..32e18661d0 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -481,16 +485,25 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
--
2.20.1
[text/x-diff] v7-0003-multirange-pg_dump.patch (17.8K, ../../20191220174321.GA5956@alvherre.pgsql/4-v7-0003-multirange-pg_dump.patch)
download | inline diff:
From 20cc5ccc98f0ccf21911b37da17fc91227dac17f Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:37 -0700
Subject: [PATCH v7 03/13] multirange pg_dump
---
src/backend/commands/typecmds.c | 85 ++++++++++++--
src/backend/utils/adt/pg_upgrade_support.c | 22 ++++
src/bin/pg_dump/pg_dump.c | 130 +++++++++++++++------
src/bin/pg_dump/pg_dump.h | 1 +
src/include/catalog/binary_upgrade.h | 2 +
src/include/catalog/pg_proc.dat | 8 ++
src/include/commands/typecmds.h | 2 +
7 files changed, 205 insertions(+), 45 deletions(-)
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7a440cafd1..38948a049b 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -85,6 +85,8 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_multirange_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_multirange_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
@@ -1356,11 +1358,11 @@ DefineRange(CreateRangeStmt *stmt)
char *typeName;
Oid typeNamespace;
Oid typoid;
- Oid mltrngtypoid;
char *rangeArrayName;
char *multirangeTypeName;
char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeOid;
Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
@@ -1530,8 +1532,11 @@ DefineRange(CreateRangeStmt *stmt)
/* Allocate OID for array type */
rangeArrayOid = AssignTypeArrayOid();
+ /* Allocate OID for multirange type */
+ multirangeOid = AssignTypeMultirangeOid();
+
/* Allocate OID for multirange array type */
- multirangeArrayOid = AssignTypeArrayOid();
+ multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
address =
@@ -1573,7 +1578,7 @@ DefineRange(CreateRangeStmt *stmt)
multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
mltrngaddress =
- TypeCreate(InvalidOid, /* no predetermined type OID */
+ TypeCreate(multirangeOid, /* force assignment of this type OID */
multirangeTypeName, /* type name */
typeNamespace, /* namespace */
InvalidOid, /* relation oid (n/a here) */
@@ -1604,11 +1609,11 @@ DefineRange(CreateRangeStmt *stmt)
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
InvalidOid); /* type's collation (ranges never have one) */
- mltrngtypoid = mltrngaddress.objectId;
+ Assert(multirangeOid == mltrngaddress.objectId);
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff, mltrngtypoid);
+ rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
@@ -1671,7 +1676,7 @@ DefineRange(CreateRangeStmt *stmt)
InvalidOid, /* typmodin procedure - none */
InvalidOid, /* typmodout procedure - none */
F_ARRAY_TYPANALYZE, /* analyze procedure */
- mltrngtypoid, /* element type ID */
+ multirangeOid, /* element type ID */
true, /* yes this is an array type */
InvalidOid, /* no further array type */
InvalidOid, /* base type ID */
@@ -1688,7 +1693,7 @@ DefineRange(CreateRangeStmt *stmt)
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
makeMultirangeConstructors(multirangeTypeName, typeNamespace,
- mltrngtypoid, rangeArrayOid);
+ multirangeOid, rangeArrayOid);
pfree(multirangeTypeName);
pfree(multirangeArrayName);
@@ -2269,6 +2274,72 @@ AssignTypeArrayOid(void)
return type_array_oid;
}
+/*
+ * AssignTypeMultirangeOid
+ *
+ * Pre-assign the range type's multirange OID for use in pg_type.oid
+ */
+Oid
+AssignTypeMultirangeOid(void)
+{
+ Oid type_multirange_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_multirange_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
+
+ type_multirange_oid = binary_upgrade_next_multirange_pg_type_oid;
+ binary_upgrade_next_multirange_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_oid;
+}
+
+/*
+ * AssignTypeMultirangeArrayOid
+ *
+ * Pre-assign the range type's multirange array OID for use in pg_type.typarray
+ */
+Oid
+AssignTypeMultirangeArrayOid(void)
+{
+ Oid type_multirange_array_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_multirange_array_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
+
+ type_multirange_array_oid = binary_upgrade_next_multirange_array_pg_type_oid;
+ binary_upgrade_next_multirange_array_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_array_oid;
+}
+
/*-------------------------------------------------------------------
* DefineCompositeType
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 99db5ba389..d980b96f48 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -51,6 +51,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
+Datum
+binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_multirange_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
+binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_multirange_array_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
Datum
binary_upgrade_set_next_toast_pg_type_oid(PG_FUNCTION_ARGS)
{
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 08658c8e86..4a0a03d50c 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -274,7 +274,8 @@ static void dumpSearchPath(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type);
+ bool force_array_type,
+ bool include_multirange_type);
static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
static void binary_upgrade_set_pg_class_oids(Archive *fout,
@@ -1583,7 +1584,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4275,15 +4276,48 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
+static Oid get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+{
+ /*
+ * If the old version didn't assign an array type, but the new version
+ * does, we must select an unused type OID to assign. This currently
+ * only happens for domains, when upgrading pre-v11 to v11 and up.
+ *
+ * Note: local state here is kind of ugly, but we must have some,
+ * since we mustn't choose the same unused OID more than once.
+ */
+ static Oid next_possible_free_oid = FirstNormalObjectId;
+ PGresult *res;
+ bool is_dup;
+
+ do
+ {
+ ++next_possible_free_oid;
+ printfPQExpBuffer(upgrade_query,
+ "SELECT EXISTS(SELECT 1 "
+ "FROM pg_catalog.pg_type "
+ "WHERE oid = '%u'::pg_catalog.oid);",
+ next_possible_free_oid);
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+ is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
+ PQclear(res);
+ } while (is_dup);
+
+ return next_possible_free_oid;
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type)
+ bool force_array_type,
+ bool include_multirange_type)
{
PQExpBuffer upgrade_query = createPQExpBuffer();
PGresult *res;
Oid pg_type_array_oid;
+ Oid pg_type_multirange_oid;
+ Oid pg_type_multirange_array_oid;
appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
@@ -4304,33 +4338,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQclear(res);
if (!OidIsValid(pg_type_array_oid) && force_array_type)
- {
- /*
- * If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
- *
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
- */
- static Oid next_possible_free_oid = FirstNormalObjectId;
- bool is_dup;
-
- do
- {
- ++next_possible_free_oid;
- printfPQExpBuffer(upgrade_query,
- "SELECT EXISTS(SELECT 1 "
- "FROM pg_catalog.pg_type "
- "WHERE oid = '%u'::pg_catalog.oid);",
- next_possible_free_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
- is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
- PQclear(res);
- } while (is_dup);
-
- pg_type_array_oid = next_possible_free_oid;
- }
+ pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
if (OidIsValid(pg_type_array_oid))
{
@@ -4341,6 +4349,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
pg_type_array_oid);
}
+ /*
+ * Pre-set the multirange type oid and its own array type oid.
+ */
+ if (include_multirange_type)
+ {
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBuffer(upgrade_query,
+ "SELECT t.oid, t.typarray "
+ "FROM pg_catalog.pg_type t "
+ "JOIN pg_catalog.pg_range r "
+ "ON t.oid = r.mltrngtypid "
+ "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
+ pg_type_oid);
+
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+
+ pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+ pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
+
+ PQclear(res);
+ }
+ else
+ {
+ pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ }
+
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_oid);
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_array_oid);
+ }
+
destroyPQExpBuffer(upgrade_query);
}
@@ -4374,7 +4422,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
- pg_type_oid, false);
+ pg_type_oid, false, false);
if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
{
@@ -4923,6 +4971,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
@@ -10245,7 +10298,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
qualtypname);
@@ -10371,7 +10424,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, true);
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
qualtypname);
@@ -10477,7 +10530,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
qualtypname);
@@ -10682,7 +10735,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
@@ -10869,7 +10922,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- true); /* force array type */
+ true, /* force array type */
+ false); /* force multirange type */
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -11057,7 +11111,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
{
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
}
@@ -11331,7 +11385,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
stinfo->baseType->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
fmtQualifiedDumpable(stinfo));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 7b2c1524a5..e27a752b6d 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -175,6 +175,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 2927b7a4d3..2b6e87bb84 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,6 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_multirange_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_multirange_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 4311db01ff..5150fb3ea3 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10366,6 +10366,14 @@
proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
+{ oid => '8144', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
+{ oid => '8145', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
{ oid => '3585', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 8af0665f29..2b9115b6a0 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
+extern Oid AssignTypeMultirangeOid(void);
+extern Oid AssignTypeMultirangeArrayOid(void);
extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
--
2.20.1
[text/x-diff] v7-0004-multirange-docs.patch (25.5K, ../../20191220174321.GA5956@alvherre.pgsql/5-v7-0004-multirange-docs.patch)
download | inline diff:
From 7bfb960aa885de5834b4a0bdcce57ead7eaf8962 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Sat, 21 Sep 2019 21:28:47 -0700
Subject: [PATCH v7 04/13] multirange docs
---
doc/src/sgml/extend.sgml | 28 +++-
doc/src/sgml/func.sgml | 302 +++++++++++++++++++++++++++++++----
doc/src/sgml/rangetypes.sgml | 47 +++++-
3 files changed, 342 insertions(+), 35 deletions(-)
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index a3046f22d0..7ea2f65778 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -229,9 +229,9 @@
</indexterm>
<para>
- Five pseudo-types of special interest are <type>anyelement</type>,
+ Six pseudo-types of special interest are <type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
- and <type>anyrange</type>,
+ <type>anyrange</type>, and <type>anymultirange</type>.
which are collectively called <firstterm>polymorphic types</firstterm>.
Any function declared using these types is said to be
a <firstterm>polymorphic function</firstterm>. A polymorphic function can
@@ -250,15 +250,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type>, the actual range type in
- the <type>anyrange</type> positions must be a range whose subtype is
- the same type appearing in the <type>anyelement</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -267,6 +267,20 @@
be an enum type.
</para>
+ <para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type>, the actual range type in
+ the <type>anyrange</type> positions must be a range whose subtype is
+ the same type appearing in the <type>anyelement</type> positions.
+ The type <type>anymultirange</type> accepts a multirange
+ whose contents match the other polymorphic types.
+ That is it must hold ranges matching <type>anyrange</type>
+ and base type elements matching <type>anyelement</type> (if either of
+ those are present). If <type>anyarray</type> is present its elements
+ must match the base type of the <type>anyrange</type> and/or the base
+ type of the ranges in an <type>anymultirange</type>.
+ </para>
+
<para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 57a1539506..af547a2232 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14566,7 +14566,9 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14576,7 +14578,7 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14584,136 +14586,247 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>f</literal></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)</literallayout></entry>
<entry><literal>[5,20)</literal></entry>
</row>
+ <row>
+ <entry> <literal>@+</literal> </entry>
+ <entry>safe union</entry>
+ <entry>
+ <literallayout class="monospaced">numrange(5,10) @+ numrange(15,20)
+numrange(5,10) @+ '{[15,20)}'::nummultirange
+'{[5,10)}'::nummultirange @+ numrange(15,20)
+'{[5,10)}'::nummultirange @+ '{[15,20)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literal>{[5,10), [15,20)}</literal></entry>
+ </row>
+
<row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)</literallayout></entry>
<entry><literal>[10,15)</literal></entry>
</row>
+ <row>
+ <entry> <literal>@*</literal> </entry>
+ <entry>safe intersection</entry>
+ <entry>
+ <literallayout class="monospaced">int8range(5,15) @* int8range(10,20)
+int8range(5,15) @* '{[10,20)}'::int8multirange
+'{[5,15)}'::int8multirange @* int8range(10,20)
+'{[5,15)}'::int8multirange @* '{[10,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literal>{[10,15)}</literal></entry>
+ </row>
+
<row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)</literallayout></entry>
<entry><literal>[5,10)</literal></entry>
</row>
+ <row>
+ <entry> <literal>@-</literal> </entry>
+ <entry>safe difference</entry>
+ <entry>
+ <literallayout class="monospaced">int8range(5,20) @- int8range(10,15)
+int8range(5,20) @- '{[10,15)}'::int8multirange
+'{[5,20)}'::int8multirange @- int8range(10,15)
+'{[5,20)}'::int8multirange @- '{[10,15)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literal>{[5,10), [15,20)}</literal></entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -14729,19 +14842,28 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. The safe versions of union, intersection, and difference
+ return multiranges, so they can handle gaps without failing.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14790,6 +14912,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower(numrange(1.1,2.2))</literal></entry>
<entry><literal>1.1</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14801,6 +14934,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper(numrange(1.1,2.2))</literal></entry>
<entry><literal>2.2</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14812,6 +14956,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>isempty(numrange(1.1,2.2))</literal></entry>
<entry><literal>false</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14823,6 +14978,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower_inc(numrange(1.1,2.2))</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14834,6 +15000,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper_inc(numrange(1.1,2.2))</literal></entry>
<entry><literal>false</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14845,6 +15022,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower_inf('(,)'::daterange)</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14856,6 +15044,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper_inf('(,)'::daterange)</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14867,16 +15066,27 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -15195,6 +15405,44 @@ NULL baz</literallayout>(3 rows)</entry>
</entry>
</row>
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
<row>
<entry>
<indexterm>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index 3a034d9b06..3e37b352fc 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -235,10 +242,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -270,6 +297,19 @@ SELECT int8range(1, 14, '(]');
-- Using NULL for either bound causes the range to be unbounded on that side.
SELECT numrange(NULL, 2.2);
+</programlisting>
+ </para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
</programlisting>
</para>
</sect2>
@@ -344,6 +384,11 @@ SELECT '[1.234, 5.678]'::floatrange;
function in this example.
</para>
+ <para>
+ When you define your own range your automatically get a corresponding
+ multirange type.
+ </para>
+
<para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
--
2.20.1
[text/x-diff] v7-0005-Simplify-makeMultirangeConstructors.patch (6.2K, ../../20191220174321.GA5956@alvherre.pgsql/6-v7-0005-Simplify-makeMultirangeConstructors.patch)
download | inline diff:
From 4a1189b23402f1404d429c9611851e2e5b7ce352 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 28 Nov 2019 19:08:43 -0300
Subject: [PATCH v7 05/13] Simplify makeMultirangeConstructors
---
src/backend/commands/typecmds.c | 134 +++++++++++++++++++-------------
1 file changed, 80 insertions(+), 54 deletions(-)
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 38948a049b..1b012c9cad 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1784,70 +1784,96 @@ static void
makeMultirangeConstructors(const char *name, Oid namespace,
Oid multirangeOid, Oid rangeArrayOid)
{
- static const char *const prosrc[2] = {"multirange_constructor0",
- "multirange_constructor1"};
- static const int pronargs[2] = {0, 1};
-
- Oid constructorArgTypes = rangeArrayOid;
ObjectAddress myself,
referenced;
- int i;
-
- Datum allParamTypes[1] = {ObjectIdGetDatum(rangeArrayOid)};
- ArrayType *allParameterTypes = construct_array(allParamTypes, 1, OIDOID,
- sizeof(Oid), true, 'i');
- Datum constructorAllParamTypes[2] = {PointerGetDatum(NULL), PointerGetDatum(allParameterTypes)};
-
- Datum paramModes[1] = {CharGetDatum(FUNC_PARAM_VARIADIC)};
- ArrayType *parameterModes = construct_array(paramModes, 1, CHAROID,
- 1, true, 'c');
- Datum constructorParamModes[2] = {PointerGetDatum(NULL), PointerGetDatum(parameterModes)};
+ oidvector *argtypes;
+ Datum allParamTypes;
+ ArrayType *allParameterTypes;
+ Datum paramModes;
+ ArrayType *parameterModes;
referenced.classId = TypeRelationId;
referenced.objectId = multirangeOid;
referenced.objectSubId = 0;
- for (i = 0; i < lengthof(prosrc); i++)
- {
- oidvector *constructorArgTypesVector;
+ argtypes = buildoidvector(NULL, 0);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor0", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
- constructorArgTypesVector = buildoidvector(&constructorArgTypes,
- pronargs[i]);
+ /*
+ * Make the constructor internally-dependent on the multirange type so
+ * that they go away silently when the type is dropped. Note that pg_dump
+ * depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
- myself = ProcedureCreate(name, /* name: same as multirange type */
- namespace, /* namespace */
- false, /* replace */
- false, /* returns set */
- multirangeOid, /* return type */
- BOOTSTRAP_SUPERUSERID, /* proowner */
- INTERNALlanguageId, /* language */
- F_FMGR_INTERNAL_VALIDATOR, /* language validator */
- prosrc[i], /* prosrc */
- NULL, /* probin */
- PROKIND_FUNCTION,
- false, /* security_definer */
- false, /* leakproof */
- false, /* isStrict */
- PROVOLATILE_IMMUTABLE, /* volatility */
- PROPARALLEL_SAFE, /* parallel safety */
- constructorArgTypesVector, /* parameterTypes */
- constructorAllParamTypes[i], /* allParameterTypes */
- constructorParamModes[i], /* parameterModes */
- PointerGetDatum(NULL), /* parameterNames */
- NIL, /* parameterDefaults */
- PointerGetDatum(NULL), /* trftypes */
- PointerGetDatum(NULL), /* proconfig */
- InvalidOid, /* prosupport */
- 1.0, /* procost */
- 0.0); /* prorows */
+ pfree(argtypes);
- /*
- * Make the constructors internally-dependent on the multirange type
- * so that they go away silently when the type is dropped. Note that
- * pg_dump depends on this choice to avoid dumping the constructors.
- */
- recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
- }
+ argtypes = buildoidvector(&rangeArrayOid, 1);
+ allParamTypes = ObjectIdGetDatum(rangeArrayOid);
+ allParameterTypes = construct_array(&allParamTypes,
+ 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
+ parameterModes = construct_array(¶mModes, 1, CHAROID,
+ 1, true, 'c');
+
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor1", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(allParameterTypes), /* allParameterTypes */
+ PointerGetDatum(parameterModes), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+
+ pfree(argtypes);
+ pfree(allParameterTypes);
+ pfree(parameterModes);
}
/*
--
2.20.1
[text/x-diff] v7-0006-silence-compiler-warning.patch (783B, ../../20191220174321.GA5956@alvherre.pgsql/7-v7-0006-silence-compiler-warning.patch)
download | inline diff:
From 0f411ccb39adc38ffa51037d04c818726aee834e Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 28 Nov 2019 19:08:46 -0300
Subject: [PATCH v7 06/13] silence compiler warning
---
src/backend/parser/parse_coerce.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 49c75dc12d..b43b2dc612 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1592,6 +1592,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
}
}
+ else
+ range_typelem = InvalidOid; /* keep compiler quiet */
/* Get the element type based on the multirange type, if we have one */
if (OidIsValid(multirange_typeid))
--
2.20.1
[text/x-diff] v7-0007-Be-less-verbose-on-variable-names.patch (4.8K, ../../20191220174321.GA5956@alvherre.pgsql/8-v7-0007-Be-less-verbose-on-variable-names.patch)
download | inline diff:
From 7837930d4c44922fe30031c13b5a425d66ce5e6c Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 28 Nov 2019 19:30:03 -0300
Subject: [PATCH v7 07/13] Be less verbose on variable names
---
src/backend/commands/typecmds.c | 23 +++++++++-------------
src/backend/utils/adt/pg_upgrade_support.c | 4 ++--
src/include/catalog/binary_upgrade.h | 4 ++--
3 files changed, 13 insertions(+), 18 deletions(-)
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 1b012c9cad..4ca2d3364b 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -85,8 +85,8 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
-Oid binary_upgrade_next_multirange_pg_type_oid = InvalidOid;
-Oid binary_upgrade_next_multirange_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
@@ -1529,13 +1529,9 @@ DefineRange(CreateRangeStmt *stmt)
/* alignment must be 'i' or 'd' for ranges */
alignment = (subtypalign == 'd') ? 'd' : 'i';
- /* Allocate OID for array type */
+ /* Allocate OID for array type, its multirange and its multirange array */
rangeArrayOid = AssignTypeArrayOid();
-
- /* Allocate OID for multirange type */
multirangeOid = AssignTypeMultirangeOid();
-
- /* Allocate OID for multirange array type */
multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
@@ -1574,7 +1570,6 @@ DefineRange(CreateRangeStmt *stmt)
Assert(typoid == address.objectId);
/* Create the multirange that goes with it */
-
multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
mltrngaddress =
@@ -2313,13 +2308,13 @@ AssignTypeMultirangeOid(void)
/* Use binary-upgrade override for pg_type.oid? */
if (IsBinaryUpgrade)
{
- if (!OidIsValid(binary_upgrade_next_multirange_pg_type_oid))
+ if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
- type_multirange_oid = binary_upgrade_next_multirange_pg_type_oid;
- binary_upgrade_next_multirange_pg_type_oid = InvalidOid;
+ type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
+ binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
}
else
{
@@ -2346,13 +2341,13 @@ AssignTypeMultirangeArrayOid(void)
/* Use binary-upgrade override for pg_type.oid? */
if (IsBinaryUpgrade)
{
- if (!OidIsValid(binary_upgrade_next_multirange_array_pg_type_oid))
+ if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
- type_multirange_array_oid = binary_upgrade_next_multirange_array_pg_type_oid;
- binary_upgrade_next_multirange_array_pg_type_oid = InvalidOid;
+ type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
+ binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
}
else
{
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index d980b96f48..418c26c81b 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -57,7 +57,7 @@ binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
Oid typoid = PG_GETARG_OID(0);
CHECK_IS_BINARY_UPGRADE;
- binary_upgrade_next_multirange_pg_type_oid = typoid;
+ binary_upgrade_next_mrng_pg_type_oid = typoid;
PG_RETURN_VOID();
}
@@ -68,7 +68,7 @@ binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
Oid typoid = PG_GETARG_OID(0);
CHECK_IS_BINARY_UPGRADE;
- binary_upgrade_next_multirange_array_pg_type_oid = typoid;
+ binary_upgrade_next_mrng_array_pg_type_oid = typoid;
PG_RETURN_VOID();
}
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 2b6e87bb84..ba132ddf23 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,8 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
-extern PGDLLIMPORT Oid binary_upgrade_next_multirange_pg_type_oid;
-extern PGDLLIMPORT Oid binary_upgrade_next_multirange_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
--
2.20.1
[text/x-diff] v7-0008-Protect-comment-against-pgindent.patch (1.3K, ../../20191220174321.GA5956@alvherre.pgsql/9-v7-0008-Protect-comment-against-pgindent.patch)
download | inline diff:
From b162aa439321b717fcc390edc5b024e37659fcbb Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 28 Nov 2019 19:36:53 -0300
Subject: [PATCH v7 08/13] Protect comment against pgindent
---
src/backend/utils/adt/multirangetypes.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 7717acb3b1..b8ff39077c 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -1136,15 +1136,16 @@ multirange_intersect_multirange_internal(Oid mltrngtypoid, TypeCacheEntry *range
if (range_count1 == 0 || range_count2 == 0)
return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
- /*
+ /*-----------------------------------------------
* Worst case is a stitching pattern like this:
*
* mr1: --- --- --- ---
* mr2: --- --- ---
* mr3: - - - - - -
*
- * That seems to be range_count1 + range_count2 - 1, but one extra won't
- * hurt.
+ * That seems to be range_count1 + range_count2 - 1,
+ * but one extra won't hurt.
+ *-----------------------------------------------
*/
ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
range_count3 = 0;
--
2.20.1
[text/x-diff] v7-0009-pgindent.patch (3.5K, ../../20191220174321.GA5956@alvherre.pgsql/10-v7-0009-pgindent.patch)
download | inline diff:
From 0223030d278ac74dc3cc95f0fff32034a40d903e Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 28 Nov 2019 19:35:43 -0300
Subject: [PATCH v7 09/13] pgindent
---
src/backend/utils/adt/multirangetypes.c | 14 +++++++-------
src/backend/utils/fmgr/funcapi.c | 12 ++++++------
src/bin/pg_dump/pg_dump.c | 11 ++++++-----
3 files changed, 19 insertions(+), 18 deletions(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index b8ff39077c..f4dec1bbc9 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -330,13 +330,13 @@ multirange_recv(PG_FUNCTION_ARGS)
Datum
multirange_send(PG_FUNCTION_ARGS)
{
- MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
- Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
- StringInfo buf = makeStringInfo();
- RangeType **ranges;
- int32 range_count;
- int32 i;
- MultirangeIOData *cache;
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 25623461bb..808a0e2ca7 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -630,8 +630,8 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
{
if (OidIsValid(anymultirange_type))
{
- Oid rngtype;
- Oid subtype;
+ Oid rngtype;
+ Oid subtype;
rngtype = resolve_generic_type(ANYRANGEOID,
anymultirange_type,
@@ -665,9 +665,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
{
if (OidIsValid(anyrange_type))
{
- Oid subtype;
- Oid mltrngtype;
- Oid rngtype;
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
subtype = resolve_generic_type(ANYELEMENTOID,
anyrange_type,
@@ -911,7 +911,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
{
Oid rngtype;
Oid subtype;
-
+
rngtype = resolve_generic_type(ANYRANGEOID,
anymultirange_type,
ANYMULTIRANGEOID);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 4a0a03d50c..8272a0451b 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -4276,15 +4276,16 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
-static Oid get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+static Oid
+get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
{
/*
* If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
+ * does, we must select an unused type OID to assign. This currently only
+ * happens for domains, when upgrading pre-v11 to v11 and up.
*
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
+ * Note: local state here is kind of ugly, but we must have some, since we
+ * mustn't choose the same unused OID more than once.
*/
static Oid next_possible_free_oid = FirstNormalObjectId;
PGresult *res;
--
2.20.1
[text/x-diff] v7-0010-fix-typo.patch (731B, ../../20191220174321.GA5956@alvherre.pgsql/11-v7-0010-fix-typo.patch)
download | inline diff:
From 9706285f95d797a0c1600ef5a905fb430282caac Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Tue, 17 Dec 2019 12:47:28 -0300
Subject: [PATCH v7 10/13] fix typo
---
doc/src/sgml/rangetypes.sgml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index 3e37b352fc..060a715983 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -385,7 +385,7 @@ SELECT '[1.234, 5.678]'::floatrange;
</para>
<para>
- When you define your own range your automatically get a corresponding
+ When you define your own range you automatically get a corresponding
multirange type.
</para>
--
2.20.1
[text/x-diff] v7-0011-minor-style-change.patch (1.0K, ../../20191220174321.GA5956@alvherre.pgsql/12-v7-0011-minor-style-change.patch)
download | inline diff:
From 65daf0d772fcb165cfd72e83e1ad91f4431a9c56 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Tue, 17 Dec 2019 12:47:44 -0300
Subject: [PATCH v7 11/13] minor style change
---
src/backend/catalog/pg_proc.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 80cc0721a5..cd8b6ba3cf 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -245,8 +245,9 @@ ProcedureCreate(const char *procedureName,
errmsg("cannot determine result data type"),
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
- if ((returnType == ANYRANGEOID || returnType == ANYMULTIRANGEOID
- || anyrangeOutParam) && !anyrangeInParam)
+ if ((returnType == ANYRANGEOID ||
+ returnType == ANYMULTIRANGEOID ||
+ anyrangeOutParam) && !anyrangeInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
--
2.20.1
[text/x-diff] v7-0012-remove-spurious-newline.patch (751B, ../../20191220174321.GA5956@alvherre.pgsql/13-v7-0012-remove-spurious-newline.patch)
download | inline diff:
From 7a11ec82be138cda77df12577f412dd0372b62ac Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Tue, 17 Dec 2019 13:57:54 -0300
Subject: [PATCH v7 12/13] remove spurious newline
---
src/backend/catalog/pg_range.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index d914f04858..8afb107f95 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -103,7 +103,6 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
}
/* record multirange type's dependency on the range type */
-
referencing.classId = TypeRelationId;
referencing.objectId = multirangeTypeOid;
referencing.objectSubId = 0;
--
2.20.1
[text/x-diff] v7-0013-rewrite-makeUniqueTypeName.patch (5.9K, ../../20191220174321.GA5956@alvherre.pgsql/14-v7-0013-rewrite-makeUniqueTypeName.patch)
download | inline diff:
From 31d17076f77c045c2645ab91c0dce6e9a52710ac Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 29 Nov 2019 23:03:13 -0300
Subject: [PATCH v7 13/13] rewrite makeUniqueTypeName
---
src/backend/catalog/pg_type.c | 113 ++++++++++++++++------------------
1 file changed, 53 insertions(+), 60 deletions(-)
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 73b8ace4c5..9e3b866d1c 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -27,6 +27,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
@@ -36,8 +37,8 @@
#include "utils/rel.h"
#include "utils/syscache.h"
-static int makeUniqueTypeName(char *dest, const char *typeName, size_t namelen,
- Oid typeNamespace, bool tryOriginalName);
+static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
+ bool tryOriginal);
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -786,12 +787,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
char *
makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
- char *arr = (char *) palloc(NAMEDATALEN);
- int namelen = strlen(typeName);
- int underscores;
+ char *arr;
- underscores = makeUniqueTypeName(arr, typeName, namelen, typeNamespace, false);
- if (underscores >= NAMEDATALEN - 1)
+ arr = makeUniqueTypeName(typeName, typeNamespace, false);
+ if (arr == NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -868,91 +867,85 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
* makeMultirangeTypeName
* - given a range type name, make a multirange type name for it
*
- * the caller is responsible for pfreeing the result
+ * caller is responsible for pfreeing the result
*/
char *
makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
{
- char mltrng[NAMEDATALEN];
- char *mltrngunique = (char *) palloc(NAMEDATALEN);
- int namelen = strlen(rangeTypeName);
+ char *buf;
+ char *mrname;
char *rangestr;
- int rangeoffset;
- int underscores;
/*
* If the range type name contains "range" then change that to
- * "multirange". Otherwise add "multirange" to the end. After that,
+ * "multirange". Otherwise add "_multirange" to the end. After that,
* prepend underscores as needed until we make a name that doesn't collide
* with anything...
*/
- strlcpy(mltrng, rangeTypeName, NAMEDATALEN);
rangestr = strstr(rangeTypeName, "range");
if (rangestr)
{
- rangeoffset = rangestr - rangeTypeName;
- strlcpy(mltrng + rangeoffset, "multi", NAMEDATALEN - rangeoffset);
- strlcpy(mltrng + rangeoffset + 5, rangestr, NAMEDATALEN - rangeoffset - 5);
- namelen += 5;
+ char *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
+
+ buf = psprintf("%s%s%s", prefix, "multi", rangestr);
}
else
- {
- strlcpy(mltrng + namelen, "multirange", NAMEDATALEN - namelen);
- namelen += 10;
- }
+ buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
- underscores = makeUniqueTypeName(mltrngunique, mltrng, namelen, typeNamespace, true);
- if (underscores >= NAMEDATALEN - 1)
+ /* clip it at NAMEDATALEN-1 bytes */
+ buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
+
+ mrname = makeUniqueTypeName(buf, typeNamespace, true);
+ if (mrname == NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form multirange type name for type \"%s\"",
rangeTypeName)));
- return mltrngunique;
+ return mrname;
}
/*
- * makeUniqueTypeName: Prepend underscores as needed until we make a name that
- * doesn't collide with anything. Tries the original typeName if requested.
+ * makeUniqueTypeName
+ * Generate a unique name for a prospective new type
*
- * Returns the number of underscores added.
+ * Given a typeName, return a new palloc'ed name by prepending underscores
+ * until a non-conflicting name results.
+ *
+ * If tryOriginal, first try with zero underscores.
*/
-int
-makeUniqueTypeName(char *dest, const char *typeName, size_t namelen, Oid typeNamespace,
- bool tryOriginalName)
+static char *
+makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
{
int i;
+ int namelen;
+ char dest[NAMEDATALEN];
- for (i = 0; i < NAMEDATALEN - 1; i++)
+ Assert(strlen(typeName) <= NAMEDATALEN - 1);
+
+ if (tryOriginal &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(typeName);
+
+ /*
+ * The idea is to prepend underscores as needed until we make a name that
+ * doesn't collide with anything ...
+ */
+ namelen = strlen(typeName);
+ for (i = 1; i < NAMEDATALEN - 1; i++)
{
- if (i == 0)
- {
- if (tryOriginalName &&
- !SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(typeName),
- ObjectIdGetDatum(typeNamespace)))
- {
- strcpy(dest, typeName);
- break;
- }
+ dest[i - 1] = '_';
+ strlcpy(dest + i, typeName, NAMEDATALEN - i);
+ if (namelen + i >= NAMEDATALEN)
+ truncate_identifier(dest, NAMEDATALEN, false);
- }
- else
- {
- dest[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(dest + i, typeName);
- else
- {
- strlcpy(dest + i, typeName, NAMEDATALEN);
- truncate_identifier(dest, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(dest),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(dest);
}
- return i;
+ return NULL;
}
--
2.20.1
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2019-12-20 18:19 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2 siblings, 1 reply; 152+ messages in thread
From: Pavel Stehule @ 2019-12-20 18:19 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Paul A Jungwirth <pj@illuminatedcomputing.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
pá 20. 12. 2019 v 18:43 odesílatel Alvaro Herrera <alvherre@2ndquadrant.com>
napsal:
> I took the liberty of rebasing this series on top of recent branch
> master. The first four are mostly Paul's originals, except for conflict
> fixes; the rest are changes I'm proposing as I go along figuring out the
> whole thing. (I would post just my proposed changes, if it weren't for
> the rebasing; apologies for the messiness.)
>
> I am not convinced that adding TYPTYPE_MULTIRANGE is really necessary.
> Why can't we just treat those types as TYPTYPE_RANGE and distinguish
> them using TYPCATEGORY_MULTIRANGE? That's what we do for arrays. I'll
> try to do that next.
>
> I think the algorithm for coming up with the multirange name is
> suboptimal. It works fine with the name is short enough that we can add
> a few extra letters, but otherwise the result look pretty silly. I
> think we can still improve on that. I propose to make
> makeUniqueTypeName accept a suffix, and truncate the letters that appear
> *before* the suffix rather than truncating after it's been appended.
>
> There's a number of ereport() calls that should become elog(); and a
> bunch of others that should probably acquire errcode() and be
> reformatted per our style.
>
>
> Regarding Pavel's documentation markup issue,
>
> > I am not sure how much is correct to use <literallayout
> class="monospaced">
> > in doc. It is used for ranges, and multiranges, but no in other places
>
> I looked at the generated PDF and the table looks pretty bad; the words
> in those entries overlap the words in the cell to their right. But that
> also happens with entries that do not use <literallayout class="x">!
> See [1] for an example of the existing docs being badly formatted. The
> docbook documentation [2] seems to suggest that what Paul used is the
> appropriate way to do this.
>
> Maybe a way is to make each entry have more than one row -- so the
> example would appear below the other three fields in its own row, and
> would be able to use the whole width of the table.
>
I had a talk with Paul about possible simplification of designed operators.
Last message from Paul was - he is working on new version.
Regards
Pavel
> [1] https://twitter.com/alvherre/status/1205563468595781633
> [2] https://tdg.docbook.org/tdg/5.1/literallayout.html
>
> --
> Álvaro Herrera https://www.2ndQuadrant.com/
> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
@ 2019-12-20 19:20 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2019-12-20 19:20 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Fri, Dec 20, 2019 at 10:19 AM Pavel Stehule <pavel.stehule@gmail.com> wrote:
> I had a talk with Paul about possible simplification of designed operators. Last message from Paul was - he is working on new version.
Thanks Alvaro & Pavel for helping move this forward. I've added the
casts but they aren't used automatically for things like
`range_union(r, mr)` or `mr + r`, even though they are implicit.
That's because the casts are for concrete types (e.g. int4range ->
int4multirange) but the functions & operators are for polymorphic
types (anymultirange + anymultirange). So I'd like to get some
feedback about the best way to proceed.
Is it permitted to add casts with polymorphic inputs & outputs? Is
that something that we would actually want to do? I'd probably need
both the polymorphic and concrete casts so that you could still say
`int4range(1,2)::int4multirange`.
Should I change the coerce code to look for casts among concrete types
when the function has polymorphic types? I'm pretty scared to do
something like that though, both because of the complexity and lest I
cause unintended effects.
Should I just give up on implicit casts and require you to specify
one? That makes it a little more annoying to mix range & multirange
types, but personally I'm okay with that. This is my preferred
approach.
I have some time over the holidays to work on the other changes Alvaro
has suggested.
Thanks,
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2019-12-20 19:29 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 21:15 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 2 replies; 152+ messages in thread
From: Alvaro Herrera @ 2019-12-20 19:29 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On 2019-Dec-20, Paul A Jungwirth wrote:
> Is it permitted to add casts with polymorphic inputs & outputs? Is
> that something that we would actually want to do? I'd probably need
> both the polymorphic and concrete casts so that you could still say
> `int4range(1,2)::int4multirange`.
I'm embarrased to admit that I don't grok the type system well enough
(yet) to answer this question.
> Should I change the coerce code to look for casts among concrete types
> when the function has polymorphic types? I'm pretty scared to do
> something like that though, both because of the complexity and lest I
> cause unintended effects.
Yeah, I suggest to stay away from that. I think this multirange thing
is groundbreaking enough that we don't need to cause additional
potential breakage.
> Should I just give up on implicit casts and require you to specify
> one? That makes it a little more annoying to mix range & multirange
> types, but personally I'm okay with that. This is my preferred
> approach.
+1
> I have some time over the holidays to work on the other changes Alvaro
> has suggested.
I hope not to have made things worse by posting a rebase. Anyway,
that's the reason I posted my other changes separately.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2019-12-20 21:15 ` Tom Lane <tgl@sss.pgh.pa.us>
1 sibling, 0 replies; 152+ messages in thread
From: Tom Lane @ 2019-12-20 21:15 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Paul A Jungwirth <pj@illuminatedcomputing.com>; Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> On 2019-Dec-20, Paul A Jungwirth wrote:
>> Is it permitted to add casts with polymorphic inputs & outputs? Is
>> that something that we would actually want to do? I'd probably need
>> both the polymorphic and concrete casts so that you could still say
>> `int4range(1,2)::int4multirange`.
> I'm embarrased to admit that I don't grok the type system well enough
> (yet) to answer this question.
I would say no; if you want behavior like that you'd have to add code for
it into the coercion machinery, much like the casts around, say, types
record and record[] versus named composites and arrays of same. Expecting
the generic cast machinery to do the right thing would be foolhardy.
In any case, even if it did do the right thing, you'd still need
some additional polymorphic type to express the behavior you wanted,
no? So it's not clear there'd be any net savings of effort.
regards, tom lane
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-01-04 05:29 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2020-01-04 05:29 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Fri, Dec 20, 2019 at 11:29 AM Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:
> > Should I just give up on implicit casts and require you to specify
> > one? That makes it a little more annoying to mix range & multirange
> > types, but personally I'm okay with that. This is my preferred
> > approach.
>
> +1
Here is a patch adding the casts, rebasing on the latest master, and
incorporating Alvaro's changes. Per his earlier suggestion I combined
it all into one patch file, which also makes it easier to apply
rebases & updates.
My work on adding casts also removes the @+ / @- / @* operators and
adds + / - / * operators where both parameters are multiranges. I
retained other operators with mixed range/multirange parameters, both
because there are already range operators with mixed range/scalar
parameters (e.g. <@), and because it seemed like the objection to @+ /
@- / @* was not mixed parameters per se, but rather their
unguessability. Since the other operators are the same as the existing
range operators, they don't share that problem.
This still leaves the question of how best to format the docs for
these operators. I like being able to combine all the <@ variations
(e.g.) into one table row, but if that is too ugly I could give them
separate rows instead. Giving them all their own row consumes a lot of
vertical space though, and to me that makes the docs more tedious to
read & browse, so it's harder to grasp all the available range-related
operations at a glance.
I'm skeptical of changing pg_type.typtype from 'm' to 'r'. A
multirange isn't a range, so why should we give it the same type? Also
won't this break any queries that are using that column to find range
types? What is the motivation to use the same typtype for both ranges
and multiranges? (There is plenty I don't understand here, e.g. why we
have both typtype and typcategory, so maybe there is a good reason I'm
missing.)
I experimented with setting pg_type.typelem to the multirange's range
type, but it seemed to break a lot of things, and reading the code I
saw some places that treat a non-zero typelem as synonymous with being
an array. So I'm reluctant to make this change also, especially when
it is just as easy to query pg_range to get a multirange's range type.
Also range types themselves don't set typelem to their base type, and
it seems like we'd want to treat ranges and multiranges the same way
here.
Alvaro also suggested renaming pg_range.mltrngtypid to
pg_range.rngmultitypid, so it shares the same "rng" prefix as the
other columns in this table. Having a different prefix does stand out.
I've included that change in this patch too.
Yours,
Paul
Attachments:
[application/octet-stream] v8-multirange.patch (330.5K, ../../CA+renyWmmrTcyxoCozqynkpG82XDg_W5NHKLztADKkzcTLZmmA@mail.gmail.com/2-v8-multirange.patch)
download | inline diff:
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index a3046f22d0..7ea2f65778 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -229,9 +229,9 @@
</indexterm>
<para>
- Five pseudo-types of special interest are <type>anyelement</type>,
+ Six pseudo-types of special interest are <type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
- and <type>anyrange</type>,
+ <type>anyrange</type>, and <type>anymultirange</type>.
which are collectively called <firstterm>polymorphic types</firstterm>.
Any function declared using these types is said to be
a <firstterm>polymorphic function</firstterm>. A polymorphic function can
@@ -250,15 +250,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type>, the actual range type in
- the <type>anyrange</type> positions must be a range whose subtype is
- the same type appearing in the <type>anyelement</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -268,6 +268,20 @@
</para>
<para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type>, the actual range type in
+ the <type>anyrange</type> positions must be a range whose subtype is
+ the same type appearing in the <type>anyelement</type> positions.
+ The type <type>anymultirange</type> accepts a multirange
+ whose contents match the other polymorphic types.
+ That is it must hold ranges matching <type>anyrange</type>
+ and base type elements matching <type>anyelement</type> (if either of
+ those are present). If <type>anyarray</type> is present its elements
+ must match the base type of the <type>anyrange</type> and/or the base
+ type of the ranges in an <type>anymultirange</type>.
+ </para>
+
+ <para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
types are allowed. For example, a function declared as
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 57a1539506..c049d94344 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14566,7 +14566,9 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14576,7 +14578,7 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14584,136 +14586,232 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>f</literal></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)</literallayout></entry>
<entry><literal>[5,20)</literal></entry>
</row>
<row>
+ <entry> <literal>+</literal> </entry>
+ <entry>multirange union</entry>
+ <entry><literallayout class="monospaced">'{[5,10)}'::nummultirange @+ '{[15,20)}'::nummultirange</literallayout></entry>
+ <entry><literal>{[5,10), [15,20)}</literal></entry>
+ </row>
+
+ <row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)</literallayout></entry>
<entry><literal>[10,15)</literal></entry>
</row>
<row>
+ <entry> <literal>*</literal> </entry>
+ <entry>multirange intersection</entry>
+ <entry><literallayout class="monospaced">'{[5,15)}'::int8multirange @* '{[10,20)}'::int8multirange</literallayout></entry>
+ <entry><literal>{[10,15)}</literal></entry>
+ </row>
+
+ <row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)</literallayout></entry>
<entry><literal>[5,10)</literal></entry>
</row>
+ <row>
+ <entry> <literal>-</literal> </entry>
+ <entry>multirange difference</entry>
+ <entry><literallayout class="monospaced">'{[5,20)}'::int8multirange @- '{[10,15)}'::int8multirange</literallayout></entry>
+ <entry><literal>{[5,10), [15,20)}</literal></entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -14729,19 +14827,28 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. The safe versions of union, intersection, and difference
+ return multiranges, so they can handle gaps without failing.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14793,6 +14900,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14804,6 +14922,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>isempty</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14815,6 +14944,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14826,6 +14966,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14837,6 +14988,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14848,6 +15010,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14859,6 +15032,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>range_merge</function>(<type>anyrange</type>, <type>anyrange</type>)
</literal>
</entry>
@@ -14867,16 +15051,27 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -15198,6 +15393,44 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>string_agg</primary>
</indexterm>
<function>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index b75fb3a392..c8784bdce3 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -232,10 +239,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -269,6 +296,19 @@ SELECT int8range(1, 14, '(]');
SELECT numrange(NULL, 2.2);
</programlisting>
</para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
+</programlisting>
+ </para>
</sect2>
<sect2 id="rangetypes-discrete">
@@ -342,6 +382,11 @@ SELECT '[1.234, 5.678]'::floatrange;
</para>
<para>
+ When you define your own range you automatically get a corresponding
+ multirange type.
+ </para>
+
+ <para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
ordering that determines which values fall into a given range.
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 61db650755..61baffe789 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -25,6 +25,7 @@ OBJS = \
objectaddress.o \
partition.o \
pg_aggregate.o \
+ pg_cast.o \
pg_collation.o \
pg_constraint.o \
pg_conversion.o \
diff --git a/src/backend/catalog/pg_cast.c b/src/backend/catalog/pg_cast.c
new file mode 100644
index 0000000000..50b44e3a96
--- /dev/null
+++ b/src/backend/catalog/pg_cast.c
@@ -0,0 +1,116 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_cast.c
+ * routines to support manipulation of the pg_cast relation
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_cast.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/pg_cast.h"
+#include "access/table.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "tcop/tcopprot.h"
+#include "utils/builtins.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+/*
+ * ----------------------------------------------------------------
+ * CastCreate
+ * ----------------------------------------------------------------
+ */
+ObjectAddress
+CastCreate(Oid sourcetypeid, Oid targettypeid, Oid funcid, char castcontext,
+ char castmethod, DependencyType behavior)
+{
+ Relation relation;
+ HeapTuple tuple;
+ Oid castid;
+ Datum values[Natts_pg_cast];
+ bool nulls[Natts_pg_cast];
+ ObjectAddress myself,
+ referenced;
+
+ relation = table_open(CastRelationId, RowExclusiveLock);
+
+ /*
+ * Check for duplicate. This is just to give a friendly error message,
+ * the unique index would catch it anyway (so no need to sweat about race
+ * conditions).
+ */
+ tuple = SearchSysCache2(CASTSOURCETARGET,
+ ObjectIdGetDatum(sourcetypeid),
+ ObjectIdGetDatum(targettypeid));
+ if (HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("cast from type %s to type %s already exists",
+ format_type_be(sourcetypeid),
+ format_type_be(targettypeid))));
+
+ /* ready to go */
+ castid = GetNewOidWithIndex(relation, CastOidIndexId, Anum_pg_cast_oid);
+ values[Anum_pg_cast_oid - 1] = ObjectIdGetDatum(castid);
+ values[Anum_pg_cast_castsource - 1] = ObjectIdGetDatum(sourcetypeid);
+ values[Anum_pg_cast_casttarget - 1] = ObjectIdGetDatum(targettypeid);
+ values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid);
+ values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext);
+ values[Anum_pg_cast_castmethod - 1] = CharGetDatum(castmethod);
+
+ MemSet(nulls, false, sizeof(nulls));
+
+ tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
+
+ CatalogTupleInsert(relation, tuple);
+
+ /* make dependency entries */
+ myself.classId = CastRelationId;
+ myself.objectId = castid;
+ myself.objectSubId = 0;
+
+ /* dependency on source type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = sourcetypeid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, behavior);
+
+ /* dependency on target type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = targettypeid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, behavior);
+
+ /* dependency on function */
+ if (OidIsValid(funcid))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = funcid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, behavior);
+ }
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ /* Post creation hook for new cast */
+ InvokeObjectPostCreateHook(CastRelationId, castid, 0);
+
+ heap_freetuple(tuple);
+
+ table_close(relation, RowExclusiveLock);
+
+ return myself;
+}
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 9dba42671a..406f404a43 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -192,6 +192,7 @@ ProcedureCreate(const char *procedureName,
genericInParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericInParam = true;
anyrangeInParam = true;
break;
@@ -219,6 +220,7 @@ ProcedureCreate(const char *procedureName,
genericOutParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericOutParam = true;
anyrangeOutParam = true;
break;
@@ -231,10 +233,10 @@ ProcedureCreate(const char *procedureName,
/*
* Do not allow polymorphic return type unless at least one input argument
- * is polymorphic. ANYRANGE return type is even stricter: must have an
- * ANYRANGE input (since we can't deduce the specific range type from
- * ANYELEMENT). Also, do not allow return type INTERNAL unless at least
- * one input argument is INTERNAL.
+ * is polymorphic. ANYRANGE and ANYMULTIRANGE return types are even
+ * stricter: must have an ANYRANGE or ANYMULTIRANGE input (since we can't
+ * deduce the specific range type from ANYELEMENT). Also, do not allow
+ * return type INTERNAL unless at least one input argument is INTERNAL.
*/
if ((IsPolymorphicType(returnType) || genericOutParam)
&& !genericInParam)
@@ -243,12 +245,13 @@ ProcedureCreate(const char *procedureName,
errmsg("cannot determine result data type"),
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
- if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
- !anyrangeInParam)
+ if ((returnType == ANYRANGEOID ||
+ returnType == ANYMULTIRANGEOID ||
+ anyrangeOutParam) && !anyrangeInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
- errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
+ errdetail("A function returning \"anyrange\" or \"anymultirange\" must have at least one \"anyrange\" or \"anymultirange\" argument.")));
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
ereport(ERROR,
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index b5bc36c2bd..1217a6ddd0 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,12 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 8d7572da51..d74697ef9c 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -27,6 +27,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
@@ -36,6 +37,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
+ bool tryOriginal);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -783,31 +787,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
char *
makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
- char *arr = (char *) palloc(NAMEDATALEN);
- int namelen = strlen(typeName);
- int i;
-
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
+ char *arr;
- if (i >= NAMEDATALEN - 1)
+ arr = makeUniqueTypeName(typeName, typeNamespace, false);
+ if (arr == NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -878,3 +861,91 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char *buf;
+ char *mrname;
+ char *rangestr;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "_multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ char *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
+
+ buf = psprintf("%s%s%s", prefix, "multi", rangestr);
+ }
+ else
+ buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
+
+ /* clip it at NAMEDATALEN-1 bytes */
+ buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
+
+ mrname = makeUniqueTypeName(buf, typeNamespace, true);
+ if (mrname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mrname;
+}
+
+/*
+ * makeUniqueTypeName
+ * Generate a unique name for a prospective new type
+ *
+ * Given a typeName, return a new palloc'ed name by preprending underscores
+ * until a non-conflicting name results.
+ *
+ * If tryOriginal, first try with zero underscores.
+ */
+static char *
+makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
+{
+ int i;
+ int namelen;
+ char dest[NAMEDATALEN];
+
+ Assert(strlen(typeName) <= NAMEDATALEN - 1);
+
+ if (tryOriginal &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(typeName);
+
+ /*
+ * The idea is to prepend underscores as needed until we make a name that
+ * doesn't collide with anything ...
+ */
+ namelen = strlen(typeName);
+ for (i = 1; i < NAMEDATALEN - 1; i++)
+ {
+ dest[i - 1] = '_';
+ strlcpy(dest + i, typeName, NAMEDATALEN - i);
+ if (namelen + i >= NAMEDATALEN)
+ truncate_identifier(dest, NAMEDATALEN, false);
+
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(dest);
+ }
+
+ return NULL;
+}
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index c31c57e5e9..732487ff8d 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -1497,17 +1497,12 @@ CreateCast(CreateCastStmt *stmt)
char sourcetyptype;
char targettyptype;
Oid funcid;
- Oid castid;
int nargs;
char castcontext;
char castmethod;
- Relation relation;
HeapTuple tuple;
- Datum values[Natts_pg_cast];
- bool nulls[Natts_pg_cast];
- ObjectAddress myself,
- referenced;
AclResult aclresult;
+ ObjectAddress myself;
sourcetypeid = typenameTypeId(NULL, stmt->sourcetype);
targettypeid = typenameTypeId(NULL, stmt->targettype);
@@ -1731,74 +1726,8 @@ CreateCast(CreateCastStmt *stmt)
break;
}
- relation = table_open(CastRelationId, RowExclusiveLock);
-
- /*
- * Check for duplicate. This is just to give a friendly error message,
- * the unique index would catch it anyway (so no need to sweat about race
- * conditions).
- */
- tuple = SearchSysCache2(CASTSOURCETARGET,
- ObjectIdGetDatum(sourcetypeid),
- ObjectIdGetDatum(targettypeid));
- if (HeapTupleIsValid(tuple))
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_OBJECT),
- errmsg("cast from type %s to type %s already exists",
- format_type_be(sourcetypeid),
- format_type_be(targettypeid))));
-
- /* ready to go */
- castid = GetNewOidWithIndex(relation, CastOidIndexId, Anum_pg_cast_oid);
- values[Anum_pg_cast_oid - 1] = ObjectIdGetDatum(castid);
- values[Anum_pg_cast_castsource - 1] = ObjectIdGetDatum(sourcetypeid);
- values[Anum_pg_cast_casttarget - 1] = ObjectIdGetDatum(targettypeid);
- values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid);
- values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext);
- values[Anum_pg_cast_castmethod - 1] = CharGetDatum(castmethod);
-
- MemSet(nulls, false, sizeof(nulls));
-
- tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
-
- CatalogTupleInsert(relation, tuple);
-
- /* make dependency entries */
- myself.classId = CastRelationId;
- myself.objectId = castid;
- myself.objectSubId = 0;
-
- /* dependency on source type */
- referenced.classId = TypeRelationId;
- referenced.objectId = sourcetypeid;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-
- /* dependency on target type */
- referenced.classId = TypeRelationId;
- referenced.objectId = targettypeid;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-
- /* dependency on function */
- if (OidIsValid(funcid))
- {
- referenced.classId = ProcedureRelationId;
- referenced.objectId = funcid;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
- }
-
- /* dependency on extension */
- recordDependencyOnCurrentExtension(&myself, false);
-
- /* Post creation hook for new cast */
- InvokeObjectPostCreateHook(CastRelationId, castid, 0);
-
- heap_freetuple(tuple);
-
- table_close(relation, RowExclusiveLock);
-
+ myself = CastCreate(sourcetypeid, targettypeid, funcid, castcontext,
+ castmethod, DEPENDENCY_NORMAL);
return myself;
}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 52097363fd..6c9c450a8f 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -42,6 +42,7 @@
#include "catalog/objectaccess.h"
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
@@ -85,9 +86,14 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid,
+ Oid rangeArrayOid, Oid *castFuncOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -811,7 +817,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1354,7 +1361,11 @@ DefineRange(CreateRangeStmt *stmt)
Oid typeNamespace;
Oid typoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1371,6 +1382,8 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
+ Oid castFuncOid;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1519,8 +1532,10 @@ DefineRange(CreateRangeStmt *stmt)
/* alignment must be 'i' or 'd' for ranges */
alignment = (subtypalign == 'd') ? 'd' : 'i';
- /* Allocate OID for array type */
+ /* Allocate OID for array type, its multirange, and its multirange array */
rangeArrayOid = AssignTypeArrayOid();
+ multirangeOid = AssignTypeMultirangeOid();
+ multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
address =
@@ -1557,9 +1572,46 @@ DefineRange(CreateRangeStmt *stmt)
InvalidOid); /* type's collation (ranges never have one) */
Assert(typoid == address.objectId);
+ /* Create the multirange that goes with it */
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(multirangeOid, /* force assignment of this type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_MULTIRANGE, /* type-category (multirange type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ Assert(multirangeOid == mltrngaddress.objectId);
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
@@ -1600,8 +1652,53 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ multirangeOid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ multirangeOid, typoid, rangeArrayOid,
+ &castFuncOid);
+
+ /* Create cast from the range type to its multirange type */
+ CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1679,6 +1776,147 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ *
+ * Sets castFuncOid to the oid of the new constructor that can be used
+ * to cast from a range to a multirange.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
+ Oid *castFuncOid)
+{
+ ObjectAddress myself,
+ referenced;
+ oidvector *argtypes;
+ Datum allParamTypes;
+ ArrayType *allParameterTypes;
+ Datum paramModes;
+ ArrayType *parameterModes;
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ /* 0-arg constructor - for empty multiranges */
+ argtypes = buildoidvector(NULL, 0);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor0", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /*
+ * Make the constructor internally-dependent on the multirange type so
+ * that they go away silently when the type is dropped. Note that pg_dump
+ * depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+
+ /*
+ * 1-arg constructor - for casts
+ *
+ * In theory we shouldn't need both this and the vararg (n-arg) constructor,
+ * but having a separate 1-arg function lets us define casts against it.
+ */
+ argtypes = buildoidvector(&rangeOid, 1);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor1", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ true, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ *castFuncOid = myself.objectId;
+
+ /* n-arg constructor - vararg */
+ argtypes = buildoidvector(&rangeArrayOid, 1);
+ allParamTypes = ObjectIdGetDatum(rangeArrayOid);
+ allParameterTypes = construct_array(&allParamTypes,
+ 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
+ parameterModes = construct_array(¶mModes, 1, CHAROID,
+ 1, true, 'c');
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor2", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(allParameterTypes), /* allParameterTypes */
+ PointerGetDatum(parameterModes), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ pfree(allParameterTypes);
+ pfree(parameterModes);
+}
/*
* Find suitable I/O functions for a type.
@@ -2104,6 +2342,72 @@ AssignTypeArrayOid(void)
return type_array_oid;
}
+/*
+ * AssignTypeMultirangeOid
+ *
+ * Pre-assign the range type's multirange OID for use in pg_type.oid
+ */
+Oid
+AssignTypeMultirangeOid(void)
+{
+ Oid type_multirange_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
+
+ type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
+ binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_oid;
+}
+
+/*
+ * AssignTypeMultirangeArrayOid
+ *
+ * Pre-assign the range type's multirange array OID for use in pg_type.typarray
+ */
+Oid
+AssignTypeMultirangeArrayOid(void)
+{
+ Oid type_multirange_array_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
+
+ type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
+ binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_array_oid;
+}
+
/*-------------------------------------------------------------------
* DefineCompositeType
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 929f758ef4..47d17f43a4 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -187,18 +187,20 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
- targetTypeId == ANYRANGEOID)
+ targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call anyarray_in,
- * anyenum_in, or anyrange_in, which will produce an error. Also, if
- * what we have is a domain over array, enum, or range, we have to
- * relabel it to its base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * anyarray_in, anyenum_in, anyrange_in, or anymultirange_in, which
+ * will produce an error. Also, if what we have is a domain over
+ * array, enum, range, or multirange, we have to relabel it to its
+ * base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1481,6 +1483,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid array_typelem;
Oid range_typeid = InvalidOid;
Oid range_typelem;
+ Oid multirange_typeid = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
@@ -1527,6 +1531,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1577,6 +1590,39 @@ check_generic_type_consistency(const Oid *actual_arg_types,
/* otherwise, they better match */
return false;
}
+ else
+ range_typelem = InvalidOid; /* keep compiler quiet */
+ }
+
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
}
if (have_anynonarray)
@@ -1680,13 +1726,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid array_typelem;
- Oid range_typelem;
+ Oid range_typelem = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
+ bool have_anyrange = (rettype == ANYRANGEOID);
+ bool have_anymultirange = (rettype == ANYMULTIRANGEOID);
/*
* Loop through the arguments to see if we have any that are polymorphic.
@@ -1744,7 +1794,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else if (decl_type == ANYRANGEOID)
{
- have_generics = true;
+ have_generics = have_anyrange = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
@@ -1762,6 +1812,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ have_generics = have_anymultirange = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
}
/*
@@ -1812,9 +1882,12 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- if (range_typeid == ANYRANGEOID && !have_anyelement)
+ if (range_typeid == ANYRANGEOID && !have_anyelement && !have_anymultirange)
{
- /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
+ /*
+ * Special case for ANYRANGE input: okay iff no ANYELEMENT or
+ * ANYMULTIRANGE
+ */
range_typelem = ANYELEMENTOID;
}
else
@@ -1846,6 +1919,71 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(range_typeid),
format_type_be(elem_typeid))));
}
+ else
+ range_typelem = InvalidOid;
+ }
+
+ /* Get the range type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ if (multirange_typeid == ANYMULTIRANGEOID && !have_anyrange && !have_anyelement)
+ {
+ /*
+ * Special case for ANYMULTIRANGE input: okay iff no ANYRANGE or
+ * ANYELEMENT
+ */
+ multirange_typelem = ANYRANGEOID;
+ }
+ else
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+ }
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ /*
+ * Infer the element type too if needed (if there is an ANYMULTIRANGE
+ * and ANYELEMENT, with no ANYRANGE).
+ */
+ if (!OidIsValid(elem_typeid))
+ {
+ elem_typeid = get_range_subtype(range_typeid);
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
}
if (!OidIsValid(elem_typeid))
@@ -1855,6 +1993,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -1927,6 +2066,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -1958,6 +2108,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
@@ -2006,8 +2172,7 @@ resolve_generic_type(Oid declared_type,
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
- context_declared_type == ANYENUMOID ||
- context_declared_type == ANYRANGEOID)
+ context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
@@ -2019,11 +2184,37 @@ resolve_generic_type(Oid declared_type,
format_type_be(context_actual_type))));
return array_typeid;
}
+ else if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid range_typelem = get_range_subtype(context_base_type);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+ Oid range_typelem = get_range_subtype(multirange_typelem);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
- declared_type == ANYENUMOID ||
- declared_type == ANYRANGEOID)
+ declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
@@ -2051,6 +2242,19 @@ resolve_generic_type(Oid declared_type,
"anyrange", format_type_be(context_base_type))));
return range_typelem;
}
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ /* Use the element type corresponding to actual type */
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
@@ -2059,6 +2263,74 @@ resolve_generic_type(Oid declared_type,
return context_actual_type;
}
}
+ else if (declared_type == ANYRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
+ else
+ {
+ /* We can't infer a range from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
+ else if (declared_type == ANYMULTIRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typeid = get_range_multirange(context_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return multirange_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else
+ {
+ /* We can't infer a multirange from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
else
{
/* declared_type isn't polymorphic, so return it as-is */
@@ -2173,6 +2445,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..7118dd0034 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..f1342181a0
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,2171 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/hashutils.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ Oid typiofunc; /* range type's I/O function */
+ Oid typioparam; /* range type's I/O parameter */
+ FmgrInfo proc; /* lookup result for typiofunc */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData * get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks either either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str = NULL;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */ ;
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **) repalloc(ranges,
+ range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(
+ InputFunctionCall(&cache->proc, range_str_copy,
+ cache->typioparam, typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "Unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->proc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(
+ ReceiveFunctionCall(&cache->proc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range = RangeTypePGetDatum(ranges[i]);
+ uint32 range_len;
+ char *range_data;
+
+ range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
+ range_len = VARSIZE(range) - VARHDRSZ;
+ range_data = VARDATA(range);
+
+ pq_sendint32(buf, range_len);
+ pq_sendbytes(buf, range_data, range_len);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &cache->typiofunc);
+
+ if (!OidIsValid(cache->typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(cache->typiofunc, &cache->proc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ RangeType *range;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ bytelen += MAXALIGN(VARSIZE(range));
+ }
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ memcpy(ptr, range, VARSIZE(range));
+ ptr += MAXALIGN(VARSIZE(range));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of any ranges you like into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ * Returns the number of slots actually used,
+ * which may be less than input_range_count but never more.
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType * multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor2(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR, (errmsg("Can't construct multirange with a multi-dimensional array")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL element")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Construct multirange value from a single range.
+ * It'd be nice if we could just use multirange_constructor2
+ * for this case, but we need a non-variadic single-arg function
+ * to let us define a CAST from a range to its multirange.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ RangeType *range;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+
+ range = PG_GETARG_RANGE_P(0);
+
+ /* Make sure the range type matches. */
+ rngtypid = RangeTypeGetOid(range);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 1, &range));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ ereport(ERROR, (errmsg("Zero-param multirange constructor shouldn't have arguments")));
+}
+
+
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+multirange_union(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
+}
+
+/* multirange minus */
+Datum
+multirange_minus(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+multirange_intersect(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+ /*-----------------------------------------------
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1,
+ * but one extra won't hurt.
+ *-----------------------------------------------
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType * mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges1[0], ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1, MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
+ return false;
+
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
+ return false;
+ }
+
+ /* All ranges in mr2 are satisfied */
+ return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 8b26e1a93d..9362995741 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -52,6 +52,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
}
Datum
+binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
+binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_array_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
binary_upgrade_set_next_toast_pg_type_oid(PG_FUNCTION_ARGS)
{
Oid typoid = PG_GETARG_OID(0);
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index ad0e363c70..152e77f44c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -185,6 +186,30 @@ anyrange_out(PG_FUNCTION_ARGS)
}
/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
+/*
* void_in - input routine for pseudo-type VOID.
*
* We allow this so that PL functions can return VOID without any special
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 639e1dad6c..be1e044269 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -431,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -452,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -475,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -485,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -495,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -505,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -515,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -957,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -969,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -993,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1101,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1110,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1132,9 +1180,81 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
+}
+
+/* range, range -> range, range functions */
+
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
+ {
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
+ }
+
+ return false;
}
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
+}
+
+
/* Btree support */
/* btree comparator */
@@ -1144,12 +1264,6 @@ range_cmp(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
- RangeBound lower1,
- lower2;
- RangeBound upper1,
- upper2;
- bool empty1,
- empty2;
int cmp;
check_stack_depth(); /* recurses when subtype is a range type */
@@ -1160,6 +1274,28 @@ range_cmp(PG_FUNCTION_ARGS)
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+ cmp = range_cmp_internal(typcache, r1, r2);
+
+ PG_FREE_IF_COPY(r1, 0);
+ PG_FREE_IF_COPY(r2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
@@ -1177,10 +1313,7 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
- PG_FREE_IF_COPY(r1, 0);
- PG_FREE_IF_COPY(r2, 1);
-
- PG_RETURN_INT32(cmp);
+ return cmp;
}
/* inequality operators using the range_cmp function */
@@ -1218,13 +1351,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1363,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1404,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1433,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1471,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1770,6 +1917,21 @@ range_get_flags(const RangeType *range)
}
/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
+/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
* This is only needed in GiST operations, so we don't include a provision
@@ -1938,6 +2100,45 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
}
/*
+ * Compares two ranges so we can qsort them.
+ * This expects that you give qsort a RangeType **,
+ * so the RangeTypes can be in diverse locations,
+ * as long as you have a list of pointers to them all.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
+/*
* Build an empty range value of the type indicated by the typcache entry.
*/
RangeType *
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 1e3e6d3a7e..2f6257c95f 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2469,6 +2469,16 @@ type_is_range(Oid typid)
}
/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
+/*
* get_type_category_preferred
*
* Given the type OID, fetch its category and preferred-type status.
@@ -3150,6 +3160,58 @@ get_range_subtype(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngmultitypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/* ---------- PG_MULTIRANGE CACHE ---------- */
+
+/*
+ * get_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_multirange_subtype(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 53d9ddf159..fb9179c42a 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -508,6 +508,17 @@ static const struct cachedesc cacheinfo[] = {
},
4
},
+ {RangeRelationId, /* MULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngmultitypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{NamespaceRelationId, /* NAMESPACENAME */
NamespaceNameIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index cdf6331a97..6677bc8bdc 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -278,6 +278,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -294,6 +295,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -500,8 +504,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -538,7 +542,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -563,7 +567,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -588,7 +592,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -640,6 +644,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -684,6 +695,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -759,6 +777,16 @@ lookup_type_cache(Oid type_id, int flags)
}
/*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
+ /*
* If requested, get information about a domain type
*/
if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
@@ -871,6 +899,33 @@ load_rangetype_info(TypeCacheEntry *typentry)
/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Form_pg_range pg_range;
+ HeapTuple tup;
+ Oid rangetypeOid;
+
+ /* get information from pg_range */
+ tup = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(typentry->type_id));
+ /* should not fail, since we already checked typtype ... */
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+ pg_range = (Form_pg_range) GETSTRUCT(tup);
+
+ rangetypeOid = pg_range->rngtypid;
+
+ ReleaseSysCache(tup);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
+
+
+/*
* load_domaintype_info --- helper routine to set up domain constraint info
*
* Note: we assume we're called in a relatively short-lived context, so it's
@@ -1469,11 +1524,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1516,6 +1571,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index b7eee3da1d..defc801f7a 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -470,11 +470,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anynonarray = false;
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
Oid anycollation = InvalidOid;
int i;
@@ -500,12 +502,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
case ANYRANGEOID:
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_anymultirange_result = true;
+ break;
default:
break;
}
}
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/*
@@ -533,6 +538,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (!OidIsValid(anyrange_type))
anyrange_type = get_call_expr_argtype(call_expr, i);
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(anymultirange_type))
+ anymultirange_type = get_call_expr_argtype(call_expr, i);
+ break;
default:
break;
}
@@ -540,7 +549,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/* If nothing found, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -561,19 +570,131 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ anymultirange_type = mltrngtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
/* Enforce ANYNONARRAY if needed */
if (have_anynonarray && type_is_array(anyelement_type))
@@ -640,6 +761,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
default:
break;
}
@@ -664,9 +793,11 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
int inargno;
int i;
@@ -725,6 +856,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ have_anymultirange_result = true;
+ else
+ {
+ if (!OidIsValid(anymultirange_type))
+ {
+ anymultirange_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anymultirange_type))
+ return false;
+ }
+ argtypes[i] = anymultirange_type;
+ }
+ break;
default:
break;
}
@@ -734,12 +880,12 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
/* Done? */
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/* If no input polymorphics, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -760,19 +906,132 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ }
+ else
+ {
+ return false;
+ }
+ }
/* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */
@@ -792,6 +1051,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = anymultirange_type;
+ break;
default:
break;
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 799b6988b7..7237b5e52a 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -274,7 +274,8 @@ static void dumpSearchPath(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type);
+ bool force_array_type,
+ bool include_multirange_type);
static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
static void binary_upgrade_set_pg_class_oids(Archive *fout,
@@ -1583,7 +1584,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4275,15 +4276,49 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
+static Oid
+get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+{
+ /*
+ * If the old version didn't assign an array type, but the new version
+ * does, we must select an unused type OID to assign. This currently only
+ * happens for domains, when upgrading pre-v11 to v11 and up.
+ *
+ * Note: local state here is kind of ugly, but we must have some, since we
+ * mustn't choose the same unused OID more than once.
+ */
+ static Oid next_possible_free_oid = FirstNormalObjectId;
+ PGresult *res;
+ bool is_dup;
+
+ do
+ {
+ ++next_possible_free_oid;
+ printfPQExpBuffer(upgrade_query,
+ "SELECT EXISTS(SELECT 1 "
+ "FROM pg_catalog.pg_type "
+ "WHERE oid = '%u'::pg_catalog.oid);",
+ next_possible_free_oid);
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+ is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
+ PQclear(res);
+ } while (is_dup);
+
+ return next_possible_free_oid;
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type)
+ bool force_array_type,
+ bool include_multirange_type)
{
PQExpBuffer upgrade_query = createPQExpBuffer();
PGresult *res;
Oid pg_type_array_oid;
+ Oid pg_type_multirange_oid;
+ Oid pg_type_multirange_array_oid;
appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
@@ -4304,33 +4339,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQclear(res);
if (!OidIsValid(pg_type_array_oid) && force_array_type)
- {
- /*
- * If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
- *
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
- */
- static Oid next_possible_free_oid = FirstNormalObjectId;
- bool is_dup;
-
- do
- {
- ++next_possible_free_oid;
- printfPQExpBuffer(upgrade_query,
- "SELECT EXISTS(SELECT 1 "
- "FROM pg_catalog.pg_type "
- "WHERE oid = '%u'::pg_catalog.oid);",
- next_possible_free_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
- is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
- PQclear(res);
- } while (is_dup);
-
- pg_type_array_oid = next_possible_free_oid;
- }
+ pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
if (OidIsValid(pg_type_array_oid))
{
@@ -4341,6 +4350,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
pg_type_array_oid);
}
+ /*
+ * Pre-set the multirange type oid and its own array type oid.
+ */
+ if (include_multirange_type)
+ {
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBuffer(upgrade_query,
+ "SELECT t.oid, t.typarray "
+ "FROM pg_catalog.pg_type t "
+ "JOIN pg_catalog.pg_range r "
+ "ON t.oid = r.rngmultitypid "
+ "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
+ pg_type_oid);
+
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+
+ pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+ pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
+
+ PQclear(res);
+ }
+ else
+ {
+ pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ }
+
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_oid);
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_array_oid);
+ }
+
destroyPQExpBuffer(upgrade_query);
}
@@ -4374,7 +4423,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
- pg_type_oid, false);
+ pg_type_oid, false, false);
if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
{
@@ -4923,6 +4972,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
@@ -8037,9 +8091,12 @@ getProcLangs(Archive *fout, int *numProcLangs)
/*
* getCasts
- * get basic information about every cast in the system
+ * get basic information about most casts in the system
*
* numCasts is set to the number of casts read in
+ *
+ * Skip casts from a range to its multirange, since we'll create those
+ * automatically.
*/
CastInfo *
getCasts(Archive *fout, int *numCasts)
@@ -8057,7 +8114,20 @@ getCasts(Archive *fout, int *numCasts)
int i_castcontext;
int i_castmethod;
- if (fout->remoteVersion >= 80400)
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBufferStr(query, "SELECT tableoid, oid, "
+ "castsource, casttarget, castfunc, castcontext, "
+ "castmethod "
+ "FROM pg_cast c "
+ "WHERE NOT EXISTS ( "
+ "SELECT 1 FROM pg_range r "
+ "WHERE c.castsource = r.rngtypid "
+ "AND c.casttarget = r.rngmultitypid "
+ ") "
+ "ORDER BY 3,4");
+ }
+ else if (fout->remoteVersion >= 80400)
{
appendPQExpBufferStr(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
@@ -10245,7 +10315,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
qualtypname);
@@ -10371,7 +10441,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, true);
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
qualtypname);
@@ -10477,7 +10547,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
qualtypname);
@@ -10682,7 +10752,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
@@ -10869,7 +10939,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- true); /* force array type */
+ true, /* force array type */
+ false); /* force multirange type */
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -11057,7 +11128,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
{
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
}
@@ -11331,7 +11402,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
stinfo->baseType->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
fmtQualifiedDumpable(stinfo));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 21004e5078..63e0ba7116 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -175,6 +175,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 0b205a0dc6..3c113fa3bd 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -137,8 +137,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 12d94fe1b3..01d95117cd 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,6 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 13e07fc314..48a6bf5598 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -330,6 +330,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_rngmultitypid_index, 8001, on pg_range using btree(rngmultitypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index ffabe275c0..03bab74253 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..827ac4f03c 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index c67768fcab..0dafd4ec63 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -225,6 +225,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
@@ -385,6 +387,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
@@ -1203,12 +1210,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index 6ef8b8a4e7..14277c8fdf 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -512,4 +512,17 @@
{ castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
castcontext => 'e', castmethod => 'f' },
+# range to multirange
+{ castsource => 'int4range', casttarget => 'int4multirange', castfunc => 'int4multirange(int4range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'int8range', casttarget => 'int8multirange', castfunc => 'int8multirange(int8range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'numrange', casttarget => 'nummultirange', castfunc => 'nummultirange(numrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'daterange', casttarget => 'datemultirange', castfunc => 'datemultirange(daterange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tsrange', casttarget => 'tsmultirange', castfunc => 'tsmultirange(tsrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tstzrange', casttarget => 'tstzmultirange', castfunc => 'tstzmultirange(tstzrange)',
+ castcontext => 'e', castmethod => 'f' },
]
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index 1b81b52df6..f23ba28d9c 100644
--- a/src/include/catalog/pg_cast.h
+++ b/src/include/catalog/pg_cast.h
@@ -23,6 +23,8 @@
#include "catalog/genbki.h"
#include "catalog/pg_cast_d.h"
+#include "catalog/dependency.h"
+
/* ----------------
* pg_cast definition. cpp turns this into
* typedef struct FormData_pg_cast
@@ -87,4 +89,11 @@ typedef enum CoercionMethod
#endif /* EXPOSE_TO_CLIENT_CODE */
+extern ObjectAddress CastCreate(Oid sourcetypeid,
+ Oid targettypeid,
+ Oid funcid,
+ char castcontext,
+ char castmethod,
+ DependencyType behavior);
+
#endif /* PG_CAST_H */
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index ab2f50c9eb..eebebbcdb4 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 7c135da3b1..24f4a32203 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3297,5 +3297,174 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'contsel', oprjoin => 'contjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 26227df216..901fd7b303 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0b6045acb1..686eba1c4a 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9605,6 +9605,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9654,6 +9658,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9722,6 +9733,258 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8114',
+ proname => 'multirange_union', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union' },
+{ oid => '8120',
+ proname => 'multirange_minus', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus' },
+{ oid => '8126',
+ proname => 'multirange_intersect', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8146', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 't',
+ prorettype => 'int4multirange', proargtypes => 'int4range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8147', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 't',
+ prorettype => 'nummultirange', proargtypes => 'numrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8148', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 't',
+ prorettype => 'tsmultirange', proargtypes => 'tsrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8149', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 't',
+ prorettype => 'tstzmultirange', proargtypes => 'tstzrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8150', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 't',
+ prorettype => 'datemultirange', proargtypes => 'daterange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8151', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 't',
+ prorettype => 'int8multirange', proargtypes => 'int8range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8152', descr => 'anymultirange cast',
+ proname => 'anymultirange', proisstrict => 't',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
@@ -10104,6 +10367,14 @@
proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
+{ oid => '8144', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
+{ oid => '8145', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
{ oid => '3585', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index 479754c245..10060255c9 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ rngmultitypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', rngmultitypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', rngmultitypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', rngmultitypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ rngmultitypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ rngmultitypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index 98172bb1b6..3efd463c6d 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -45,6 +45,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* subtype difference as a float8, or 0 */
regproc rngsubdiff BKI_LOOKUP(pg_proc);
+
+ /* OID of the range's multirange type */
+ Oid rngmultitypid BKI_LOOKUP(pg_type);
} FormData_pg_range;
/* ----------------
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index fe2c4eabb4..59bae8232f 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -486,6 +486,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -594,5 +628,10 @@
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index e1a5ab3df3..600ac58c9d 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -258,6 +258,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -269,6 +270,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPCATEGORY_ENUM 'E'
#define TYPCATEGORY_GEOMETRIC 'G'
#define TYPCATEGORY_NETWORK 'I' /* think INET */
+#define TYPCATEGORY_MULTIRANGE 'M'
#define TYPCATEGORY_NUMERIC 'N'
#define TYPCATEGORY_PSEUDOTYPE 'P'
#define TYPCATEGORY_RANGE 'R'
@@ -284,7 +286,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#endif /* EXPOSE_TO_CLIENT_CODE */
@@ -343,4 +346,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index fc18d64347..7d0a4a9ea5 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
+extern Oid AssignTypeMultirangeOid(void);
+extern Oid AssignTypeMultirangeArrayOid(void);
extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index a216d6793d..b5ff168160 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -154,6 +154,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -179,6 +180,8 @@ extern void free_attstatsslot(AttStatsSlot *sslot);
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..8779bd1f6e
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ char vl_len_[4]; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern MultirangeType * multirange_minus_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType * multirange_intersect_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType * range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType * make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType * make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 0cbbf09033..204aee4054 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
@@ -92,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -115,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -125,6 +141,7 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
@@ -132,8 +149,12 @@ extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f27b73d76d..bc51bbeb54 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -66,6 +66,7 @@ enum SysCacheIdentifier
INDEXRELID,
LANGNAME,
LANGOID,
+ MULTIRANGETYPE,
NAMESPACENAME,
NAMESPACEOID,
OPERNAMENSP,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index 66ff17dbd5..fe240ea004 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -99,6 +99,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
/*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype;
+
+ /*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
*/
@@ -141,6 +146,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 66bf9f1211..e277329931 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -511,6 +511,8 @@ do_compile(FunctionCallInfo fcinfo,
rettypeid = INT4ARRAYOID;
else if (rettypeid == ANYRANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2494,6 +2496,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYRANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..778699a961 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -141,6 +141,7 @@ owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
owner of type deptest_range
+owner of type deptest_multirange
owner of table deptest2
owner of sequence ss1
owner of type deptest_t
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..4446f8e4c3
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,2386 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+ int4multirange
+----------------
+ {}
+(1 row)
+
+select int4range(1, 3)::int4multirange;
+ int4range
+-----------
+ {[1,3)}
+(1 row)
+
+select int4range(1, null)::int4multirange;
+ int4range
+-----------
+ {[1,)}
+(1 row)
+
+select int4range(null, null)::int4multirange;
+ int4range
+-----------
+ {(,)}
+(1 row)
+
+select 'empty'::textrange::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textrange('a', 'c')::textmultirange;
+ textrange
+-----------
+ {[a,c)}
+(1 row)
+
+select textrange('a', null)::textmultirange;
+ textrange
+-----------
+ {[a,)}
+(1 row)
+
+select textrange(null, null)::textmultirange;
+ textrange
+-----------
+ {(,)}
+(1 row)
+
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT nummultirange() + nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT nummultirange() - nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() * nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select anymultirange($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index c19740e5db..916e1016b5 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -206,6 +213,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -224,6 +233,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -329,13 +340,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
@@ -343,16 +355,19 @@ ORDER BY 2;
2502 | anyarray_recv
2312 | anyelement_in
3504 | anyenum_in
+ 8002 | anymultirange_in
2777 | anynonarray_in
3832 | anyrange_in
750 | array_in
2400 | array_recv
3506 | enum_in
3532 | enum_recv
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(13 rows)
+(16 rows)
-- Look for functions that accept cstring and are neither datatype input
-- functions nor encoding conversion functions. It's almost never a good
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 5ed6ae47ec..4a46a286a1 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1284,7 +1302,7 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
@@ -1395,6 +1413,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1403,6 +1422,16 @@ select * from outparam_succeed(int4range(1,2));
[1,2) | foo
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1411,6 +1440,7 @@ select * from inoutparam_succeed(int4range(1,2));
2 | [1,2)
(1 row)
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
select * from table_succeed(123, int4range(1,11));
@@ -1423,14 +1453,14 @@ select * from table_succeed(123, int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 070de78e85..6f7fcf6326 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
onek|t
onek2|t
path_tbl|f
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index cd7fc03b04..60a9b4d22f 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -193,18 +193,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -238,17 +239,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -316,18 +318,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -361,17 +364,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -629,3 +633,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
+ rngtypid | rngsubtype | rngmultitypid
+----------+------------+---------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index d33a4e143d..5fc0673efa 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index f86f5c5682..98a9afbc7d 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..40f4ef35b0
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,625 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+select int4range(1, 3)::int4multirange;
+select int4range(1, null)::int4multirange;
+select int4range(null, null)::int4multirange;
+select 'empty'::textrange::textmultirange;
+select textrange('a', 'c')::textmultirange;
+select textrange('a', null)::textmultirange;
+select textrange(null, null)::textmultirange;
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT nummultirange() + nummultirange();
+SELECT nummultirange() + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT nummultirange() - nummultirange();
+SELECT nummultirange() - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+SELECT nummultirange() * nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select anymultirange($1), 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 624bea46ce..23932cfef0 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -273,13 +284,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- Look for functions that accept cstring and are neither datatype input
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 2d0ec8964e..4e57352799 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -484,16 +488,25 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fed413bf98..2e34bc8b65 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -151,7 +151,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -180,7 +180,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -232,7 +232,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -261,7 +261,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -465,3 +465,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-01-10 09:38 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Pavel Stehule @ 2020-01-10 09:38 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
Hi
so 4. 1. 2020 v 6:29 odesílatel Paul A Jungwirth <
pj@illuminatedcomputing.com> napsal:
> On Fri, Dec 20, 2019 at 11:29 AM Alvaro Herrera
> <alvherre@2ndquadrant.com> wrote:
> > > Should I just give up on implicit casts and require you to specify
> > > one? That makes it a little more annoying to mix range & multirange
> > > types, but personally I'm okay with that. This is my preferred
> > > approach.
> >
> > +1
>
> Here is a patch adding the casts, rebasing on the latest master, and
> incorporating Alvaro's changes. Per his earlier suggestion I combined
> it all into one patch file, which also makes it easier to apply
> rebases & updates.
>
This patch was applied cleanly and all tests passed
>
> My work on adding casts also removes the @+ / @- / @* operators and
> adds + / - / * operators where both parameters are multiranges. I
> retained other operators with mixed range/multirange parameters, both
> because there are already range operators with mixed range/scalar
> parameters (e.g. <@), and because it seemed like the objection to @+ /
> @- / @* was not mixed parameters per se, but rather their
> unguessability. Since the other operators are the same as the existing
> range operators, they don't share that problem.
>
looks well
>
> This still leaves the question of how best to format the docs for
> these operators. I like being able to combine all the <@ variations
> (e.g.) into one table row, but if that is too ugly I could give them
> separate rows instead. Giving them all their own row consumes a lot of
> vertical space though, and to me that makes the docs more tedious to
> read & browse, so it's harder to grasp all the available range-related
> operations at a glance.
>
I have similar opinion - maybe is better do documentation for range and
multirange separately. Sometimes there are still removed operators @+
> I'm skeptical of changing pg_type.typtype from 'm' to 'r'. A
> multirange isn't a range, so why should we give it the same type? Also
> won't this break any queries that are using that column to find range
> types? What is the motivation to use the same typtype for both ranges
> and multiranges? (There is plenty I don't understand here, e.g. why we
> have both typtype and typcategory, so maybe there is a good reason I'm
> missing.)
>
If you can share TYPTYPE_RANGE in code for multiranges, then it should be
'r'. If not, then it needs own value.
> I experimented with setting pg_type.typelem to the multirange's range
> type, but it seemed to break a lot of things, and reading the code I
> saw some places that treat a non-zero typelem as synonymous with being
> an array. So I'm reluctant to make this change also, especially when
> it is just as easy to query pg_range to get a multirange's range type.
>
ok, it is unhappy, but it is true. This note should be somewhere in code,
please
> Also range types themselves don't set typelem to their base type, and
> it seems like we'd want to treat ranges and multiranges the same way
> here.
>
> Alvaro also suggested renaming pg_range.mltrngtypid to
> pg_range.rngmultitypid, so it shares the same "rng" prefix as the
> other columns in this table. Having a different prefix does stand out.
> I've included that change in this patch too.
>
Personally I have not any comments to implemented functionality.
>
> Yours,
> Paul
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-01-17 20:07 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2020-01-17 20:07 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Fri, Jan 10, 2020 at 1:38 AM Pavel Stehule <pavel.stehule@gmail.com> wrote:
>> This still leaves the question of how best to format the docs for
>> these operators. I like being able to combine all the <@ variations
>> (e.g.) into one table row, but if that is too ugly I could give them
>> separate rows instead. Giving them all their own row consumes a lot of
>> vertical space though, and to me that makes the docs more tedious to
>> read & browse, so it's harder to grasp all the available range-related
>> operations at a glance.
>
>
> I have similar opinion - maybe is better do documentation for range and multirange separately. Sometimes there are still removed operators @+
I like keeping the range/multirange operators together since they are
so similar for both types, but if others disagree I'd be grateful for
more feedback.
You're right that I left in a few references to the old @+ style
operators in the examples; I've fixed those.
> If you can share TYPTYPE_RANGE in code for multiranges, then it should be 'r'. If not, then it needs own value.
Okay. I think a new 'm' value is warranted because they are not interchangeable.
>> I experimented with setting pg_type.typelem to the multirange's range
>> type, but it seemed to break a lot of things, and reading the code I
>> saw some places that treat a non-zero typelem as synonymous with being
>> an array. So I'm reluctant to make this change also, especially when
>> it is just as easy to query pg_range to get a multirange's range type.
>
>
> ok, it is unhappy, but it is true. This note should be somewhere in code, please
I've added a comment about this. I put it at the top of DefineRange
but let me know if that's the wrong place.
The attached file is also rebased on currrent master.
Thanks!
Paul
Attachments:
[application/octet-stream] v9-multirange.patch (331.1K, ../../CA+renyVfMGDpuXjx=jUjUaw2euqkQLEDazkZhjs76qOXN0ocwA@mail.gmail.com/2-v9-multirange.patch)
download | inline diff:
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index a3046f22d0..7ea2f65778 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -229,9 +229,9 @@
</indexterm>
<para>
- Five pseudo-types of special interest are <type>anyelement</type>,
+ Six pseudo-types of special interest are <type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
- and <type>anyrange</type>,
+ <type>anyrange</type>, and <type>anymultirange</type>.
which are collectively called <firstterm>polymorphic types</firstterm>.
Any function declared using these types is said to be
a <firstterm>polymorphic function</firstterm>. A polymorphic function can
@@ -250,15 +250,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type>, the actual range type in
- the <type>anyrange</type> positions must be a range whose subtype is
- the same type appearing in the <type>anyelement</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -268,6 +268,20 @@
</para>
<para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type>, the actual range type in
+ the <type>anyrange</type> positions must be a range whose subtype is
+ the same type appearing in the <type>anyelement</type> positions.
+ The type <type>anymultirange</type> accepts a multirange
+ whose contents match the other polymorphic types.
+ That is it must hold ranges matching <type>anyrange</type>
+ and base type elements matching <type>anyelement</type> (if either of
+ those are present). If <type>anyarray</type> is present its elements
+ must match the base type of the <type>anyrange</type> and/or the base
+ type of the ranges in an <type>anymultirange</type>.
+ </para>
+
+ <para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
types are allowed. For example, a function declared as
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 72072e7545..55951dc31a 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14612,12 +14612,14 @@ NULL baz</literallayout>(3 rows)</entry>
<title>Range Functions and Operators</title>
<para>
- See <xref linkend="rangetypes"/> for an overview of range types.
+ See <xref linkend="rangetypes"/> for an overview of range and multirange types.
</para>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14627,7 +14629,7 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14635,136 +14637,232 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literal>t</literal></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
<entry><literal>f</literal></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)</literallayout></entry>
<entry><literal>[5,20)</literal></entry>
</row>
<row>
+ <entry> <literal>+</literal> </entry>
+ <entry>multirange union</entry>
+ <entry><literallayout class="monospaced">'{[5,10)}'::nummultirange + '{[15,20)}'::nummultirange</literallayout></entry>
+ <entry><literal>{[5,10), [15,20)}</literal></entry>
+ </row>
+
+ <row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)</literallayout></entry>
<entry><literal>[10,15)</literal></entry>
</row>
<row>
+ <entry> <literal>*</literal> </entry>
+ <entry>multirange intersection</entry>
+ <entry><literallayout class="monospaced">'{[5,15)}'::int8multirange * '{[10,20)}'::int8multirange</literallayout></entry>
+ <entry><literal>{[10,15)}</literal></entry>
+ </row>
+
+ <row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)</literallayout></entry>
<entry><literal>[5,10)</literal></entry>
</row>
+ <row>
+ <entry> <literal>-</literal> </entry>
+ <entry>multirange difference</entry>
+ <entry><literallayout class="monospaced">'{[5,20)}'::int8multirange - '{[10,15)}'::int8multirange</literallayout></entry>
+ <entry><literal>{[5,10), [15,20)}</literal></entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -14780,19 +14878,28 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. The safe versions of union, intersection, and difference
+ return multiranges, so they can handle gaps without failing.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14844,6 +14951,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14855,6 +14973,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>isempty</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14866,6 +14995,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14877,6 +15017,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14888,6 +15039,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14899,6 +15061,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14910,6 +15083,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>range_merge</function>(<type>anyrange</type>, <type>anyrange</type>)
</literal>
</entry>
@@ -14918,16 +15102,27 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -15249,6 +15444,44 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>string_agg</primary>
</indexterm>
<function>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index b75fb3a392..c8784bdce3 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -232,10 +239,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -269,6 +296,19 @@ SELECT int8range(1, 14, '(]');
SELECT numrange(NULL, 2.2);
</programlisting>
</para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
+</programlisting>
+ </para>
</sect2>
<sect2 id="rangetypes-discrete">
@@ -342,6 +382,11 @@ SELECT '[1.234, 5.678]'::floatrange;
</para>
<para>
+ When you define your own range you automatically get a corresponding
+ multirange type.
+ </para>
+
+ <para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
ordering that determines which values fall into a given range.
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 61db650755..61baffe789 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -25,6 +25,7 @@ OBJS = \
objectaddress.o \
partition.o \
pg_aggregate.o \
+ pg_cast.o \
pg_collation.o \
pg_constraint.o \
pg_conversion.o \
diff --git a/src/backend/catalog/pg_cast.c b/src/backend/catalog/pg_cast.c
new file mode 100644
index 0000000000..50b44e3a96
--- /dev/null
+++ b/src/backend/catalog/pg_cast.c
@@ -0,0 +1,116 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_cast.c
+ * routines to support manipulation of the pg_cast relation
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_cast.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/pg_cast.h"
+#include "access/table.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "tcop/tcopprot.h"
+#include "utils/builtins.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+/*
+ * ----------------------------------------------------------------
+ * CastCreate
+ * ----------------------------------------------------------------
+ */
+ObjectAddress
+CastCreate(Oid sourcetypeid, Oid targettypeid, Oid funcid, char castcontext,
+ char castmethod, DependencyType behavior)
+{
+ Relation relation;
+ HeapTuple tuple;
+ Oid castid;
+ Datum values[Natts_pg_cast];
+ bool nulls[Natts_pg_cast];
+ ObjectAddress myself,
+ referenced;
+
+ relation = table_open(CastRelationId, RowExclusiveLock);
+
+ /*
+ * Check for duplicate. This is just to give a friendly error message,
+ * the unique index would catch it anyway (so no need to sweat about race
+ * conditions).
+ */
+ tuple = SearchSysCache2(CASTSOURCETARGET,
+ ObjectIdGetDatum(sourcetypeid),
+ ObjectIdGetDatum(targettypeid));
+ if (HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("cast from type %s to type %s already exists",
+ format_type_be(sourcetypeid),
+ format_type_be(targettypeid))));
+
+ /* ready to go */
+ castid = GetNewOidWithIndex(relation, CastOidIndexId, Anum_pg_cast_oid);
+ values[Anum_pg_cast_oid - 1] = ObjectIdGetDatum(castid);
+ values[Anum_pg_cast_castsource - 1] = ObjectIdGetDatum(sourcetypeid);
+ values[Anum_pg_cast_casttarget - 1] = ObjectIdGetDatum(targettypeid);
+ values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid);
+ values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext);
+ values[Anum_pg_cast_castmethod - 1] = CharGetDatum(castmethod);
+
+ MemSet(nulls, false, sizeof(nulls));
+
+ tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
+
+ CatalogTupleInsert(relation, tuple);
+
+ /* make dependency entries */
+ myself.classId = CastRelationId;
+ myself.objectId = castid;
+ myself.objectSubId = 0;
+
+ /* dependency on source type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = sourcetypeid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, behavior);
+
+ /* dependency on target type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = targettypeid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, behavior);
+
+ /* dependency on function */
+ if (OidIsValid(funcid))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = funcid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, behavior);
+ }
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ /* Post creation hook for new cast */
+ InvokeObjectPostCreateHook(CastRelationId, castid, 0);
+
+ heap_freetuple(tuple);
+
+ table_close(relation, RowExclusiveLock);
+
+ return myself;
+}
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 5194dcaac0..6efcac52a0 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -192,6 +192,7 @@ ProcedureCreate(const char *procedureName,
genericInParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericInParam = true;
anyrangeInParam = true;
break;
@@ -219,6 +220,7 @@ ProcedureCreate(const char *procedureName,
genericOutParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericOutParam = true;
anyrangeOutParam = true;
break;
@@ -231,10 +233,10 @@ ProcedureCreate(const char *procedureName,
/*
* Do not allow polymorphic return type unless at least one input argument
- * is polymorphic. ANYRANGE return type is even stricter: must have an
- * ANYRANGE input (since we can't deduce the specific range type from
- * ANYELEMENT). Also, do not allow return type INTERNAL unless at least
- * one input argument is INTERNAL.
+ * is polymorphic. ANYRANGE and ANYMULTIRANGE return types are even
+ * stricter: must have an ANYRANGE or ANYMULTIRANGE input (since we can't
+ * deduce the specific range type from ANYELEMENT). Also, do not allow
+ * return type INTERNAL unless at least one input argument is INTERNAL.
*/
if ((IsPolymorphicType(returnType) || genericOutParam)
&& !genericInParam)
@@ -243,12 +245,13 @@ ProcedureCreate(const char *procedureName,
errmsg("cannot determine result data type"),
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
- if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
- !anyrangeInParam)
+ if ((returnType == ANYRANGEOID ||
+ returnType == ANYMULTIRANGEOID ||
+ anyrangeOutParam) && !anyrangeInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
- errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
+ errdetail("A function returning \"anyrange\" or \"anymultirange\" must have at least one \"anyrange\" or \"anymultirange\" argument.")));
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
ereport(ERROR,
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index b5bc36c2bd..1217a6ddd0 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,12 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 8d7572da51..d74697ef9c 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -27,6 +27,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
@@ -36,6 +37,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
+ bool tryOriginal);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -783,31 +787,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
char *
makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
- char *arr = (char *) palloc(NAMEDATALEN);
- int namelen = strlen(typeName);
- int i;
-
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
+ char *arr;
- if (i >= NAMEDATALEN - 1)
+ arr = makeUniqueTypeName(typeName, typeNamespace, false);
+ if (arr == NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -878,3 +861,91 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char *buf;
+ char *mrname;
+ char *rangestr;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "_multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ char *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
+
+ buf = psprintf("%s%s%s", prefix, "multi", rangestr);
+ }
+ else
+ buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
+
+ /* clip it at NAMEDATALEN-1 bytes */
+ buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
+
+ mrname = makeUniqueTypeName(buf, typeNamespace, true);
+ if (mrname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mrname;
+}
+
+/*
+ * makeUniqueTypeName
+ * Generate a unique name for a prospective new type
+ *
+ * Given a typeName, return a new palloc'ed name by preprending underscores
+ * until a non-conflicting name results.
+ *
+ * If tryOriginal, first try with zero underscores.
+ */
+static char *
+makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
+{
+ int i;
+ int namelen;
+ char dest[NAMEDATALEN];
+
+ Assert(strlen(typeName) <= NAMEDATALEN - 1);
+
+ if (tryOriginal &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(typeName);
+
+ /*
+ * The idea is to prepend underscores as needed until we make a name that
+ * doesn't collide with anything ...
+ */
+ namelen = strlen(typeName);
+ for (i = 1; i < NAMEDATALEN - 1; i++)
+ {
+ dest[i - 1] = '_';
+ strlcpy(dest + i, typeName, NAMEDATALEN - i);
+ if (namelen + i >= NAMEDATALEN)
+ truncate_identifier(dest, NAMEDATALEN, false);
+
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(dest);
+ }
+
+ return NULL;
+}
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index c31c57e5e9..732487ff8d 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -1497,17 +1497,12 @@ CreateCast(CreateCastStmt *stmt)
char sourcetyptype;
char targettyptype;
Oid funcid;
- Oid castid;
int nargs;
char castcontext;
char castmethod;
- Relation relation;
HeapTuple tuple;
- Datum values[Natts_pg_cast];
- bool nulls[Natts_pg_cast];
- ObjectAddress myself,
- referenced;
AclResult aclresult;
+ ObjectAddress myself;
sourcetypeid = typenameTypeId(NULL, stmt->sourcetype);
targettypeid = typenameTypeId(NULL, stmt->targettype);
@@ -1731,74 +1726,8 @@ CreateCast(CreateCastStmt *stmt)
break;
}
- relation = table_open(CastRelationId, RowExclusiveLock);
-
- /*
- * Check for duplicate. This is just to give a friendly error message,
- * the unique index would catch it anyway (so no need to sweat about race
- * conditions).
- */
- tuple = SearchSysCache2(CASTSOURCETARGET,
- ObjectIdGetDatum(sourcetypeid),
- ObjectIdGetDatum(targettypeid));
- if (HeapTupleIsValid(tuple))
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_OBJECT),
- errmsg("cast from type %s to type %s already exists",
- format_type_be(sourcetypeid),
- format_type_be(targettypeid))));
-
- /* ready to go */
- castid = GetNewOidWithIndex(relation, CastOidIndexId, Anum_pg_cast_oid);
- values[Anum_pg_cast_oid - 1] = ObjectIdGetDatum(castid);
- values[Anum_pg_cast_castsource - 1] = ObjectIdGetDatum(sourcetypeid);
- values[Anum_pg_cast_casttarget - 1] = ObjectIdGetDatum(targettypeid);
- values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid);
- values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext);
- values[Anum_pg_cast_castmethod - 1] = CharGetDatum(castmethod);
-
- MemSet(nulls, false, sizeof(nulls));
-
- tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
-
- CatalogTupleInsert(relation, tuple);
-
- /* make dependency entries */
- myself.classId = CastRelationId;
- myself.objectId = castid;
- myself.objectSubId = 0;
-
- /* dependency on source type */
- referenced.classId = TypeRelationId;
- referenced.objectId = sourcetypeid;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-
- /* dependency on target type */
- referenced.classId = TypeRelationId;
- referenced.objectId = targettypeid;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-
- /* dependency on function */
- if (OidIsValid(funcid))
- {
- referenced.classId = ProcedureRelationId;
- referenced.objectId = funcid;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
- }
-
- /* dependency on extension */
- recordDependencyOnCurrentExtension(&myself, false);
-
- /* Post creation hook for new cast */
- InvokeObjectPostCreateHook(CastRelationId, castid, 0);
-
- heap_freetuple(tuple);
-
- table_close(relation, RowExclusiveLock);
-
+ myself = CastCreate(sourcetypeid, targettypeid, funcid, castcontext,
+ castmethod, DEPENDENCY_NORMAL);
return myself;
}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 52097363fd..a8dea8d58e 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -42,6 +42,7 @@
#include "catalog/objectaccess.h"
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
@@ -85,9 +86,14 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid,
+ Oid rangeArrayOid, Oid *castFuncOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -811,7 +817,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1346,6 +1353,11 @@ checkEnumOwner(HeapTuple tup)
/*
* DefineRange
* Registers a new range type.
+ *
+ * Perhaps it might be worthwhile to set pg_type.typelem to the base type,
+ * and likewise on multiranges to set it to the range type. But having a
+ * non-zero typelem is treated elsewhere as a synonym for being an array,
+ * and users might have queries with that same assumption.
*/
ObjectAddress
DefineRange(CreateRangeStmt *stmt)
@@ -1354,7 +1366,11 @@ DefineRange(CreateRangeStmt *stmt)
Oid typeNamespace;
Oid typoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1371,6 +1387,8 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
+ Oid castFuncOid;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1519,8 +1537,10 @@ DefineRange(CreateRangeStmt *stmt)
/* alignment must be 'i' or 'd' for ranges */
alignment = (subtypalign == 'd') ? 'd' : 'i';
- /* Allocate OID for array type */
+ /* Allocate OID for array type, its multirange, and its multirange array */
rangeArrayOid = AssignTypeArrayOid();
+ multirangeOid = AssignTypeMultirangeOid();
+ multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
address =
@@ -1557,9 +1577,46 @@ DefineRange(CreateRangeStmt *stmt)
InvalidOid); /* type's collation (ranges never have one) */
Assert(typoid == address.objectId);
+ /* Create the multirange that goes with it */
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(multirangeOid, /* force assignment of this type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_MULTIRANGE, /* type-category (multirange type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ Assert(multirangeOid == mltrngaddress.objectId);
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
@@ -1600,8 +1657,53 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ multirangeOid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ multirangeOid, typoid, rangeArrayOid,
+ &castFuncOid);
+
+ /* Create cast from the range type to its multirange type */
+ CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1679,6 +1781,147 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ *
+ * Sets castFuncOid to the oid of the new constructor that can be used
+ * to cast from a range to a multirange.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
+ Oid *castFuncOid)
+{
+ ObjectAddress myself,
+ referenced;
+ oidvector *argtypes;
+ Datum allParamTypes;
+ ArrayType *allParameterTypes;
+ Datum paramModes;
+ ArrayType *parameterModes;
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ /* 0-arg constructor - for empty multiranges */
+ argtypes = buildoidvector(NULL, 0);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor0", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /*
+ * Make the constructor internally-dependent on the multirange type so
+ * that they go away silently when the type is dropped. Note that pg_dump
+ * depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+
+ /*
+ * 1-arg constructor - for casts
+ *
+ * In theory we shouldn't need both this and the vararg (n-arg) constructor,
+ * but having a separate 1-arg function lets us define casts against it.
+ */
+ argtypes = buildoidvector(&rangeOid, 1);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor1", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ true, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ *castFuncOid = myself.objectId;
+
+ /* n-arg constructor - vararg */
+ argtypes = buildoidvector(&rangeArrayOid, 1);
+ allParamTypes = ObjectIdGetDatum(rangeArrayOid);
+ allParameterTypes = construct_array(&allParamTypes,
+ 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
+ parameterModes = construct_array(¶mModes, 1, CHAROID,
+ 1, true, 'c');
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor2", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(allParameterTypes), /* allParameterTypes */
+ PointerGetDatum(parameterModes), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ pfree(allParameterTypes);
+ pfree(parameterModes);
+}
/*
* Find suitable I/O functions for a type.
@@ -2104,6 +2347,72 @@ AssignTypeArrayOid(void)
return type_array_oid;
}
+/*
+ * AssignTypeMultirangeOid
+ *
+ * Pre-assign the range type's multirange OID for use in pg_type.oid
+ */
+Oid
+AssignTypeMultirangeOid(void)
+{
+ Oid type_multirange_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
+
+ type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
+ binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_oid;
+}
+
+/*
+ * AssignTypeMultirangeArrayOid
+ *
+ * Pre-assign the range type's multirange array OID for use in pg_type.typarray
+ */
+Oid
+AssignTypeMultirangeArrayOid(void)
+{
+ Oid type_multirange_array_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
+
+ type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
+ binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_array_oid;
+}
+
/*-------------------------------------------------------------------
* DefineCompositeType
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 929f758ef4..47d17f43a4 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -187,18 +187,20 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
- targetTypeId == ANYRANGEOID)
+ targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call anyarray_in,
- * anyenum_in, or anyrange_in, which will produce an error. Also, if
- * what we have is a domain over array, enum, or range, we have to
- * relabel it to its base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * anyarray_in, anyenum_in, anyrange_in, or anymultirange_in, which
+ * will produce an error. Also, if what we have is a domain over
+ * array, enum, range, or multirange, we have to relabel it to its
+ * base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1481,6 +1483,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid array_typelem;
Oid range_typeid = InvalidOid;
Oid range_typelem;
+ Oid multirange_typeid = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
@@ -1527,6 +1531,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1577,6 +1590,39 @@ check_generic_type_consistency(const Oid *actual_arg_types,
/* otherwise, they better match */
return false;
}
+ else
+ range_typelem = InvalidOid; /* keep compiler quiet */
+ }
+
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
}
if (have_anynonarray)
@@ -1680,13 +1726,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid array_typelem;
- Oid range_typelem;
+ Oid range_typelem = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
+ bool have_anyrange = (rettype == ANYRANGEOID);
+ bool have_anymultirange = (rettype == ANYMULTIRANGEOID);
/*
* Loop through the arguments to see if we have any that are polymorphic.
@@ -1744,7 +1794,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else if (decl_type == ANYRANGEOID)
{
- have_generics = true;
+ have_generics = have_anyrange = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
@@ -1762,6 +1812,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ have_generics = have_anymultirange = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
}
/*
@@ -1812,9 +1882,12 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- if (range_typeid == ANYRANGEOID && !have_anyelement)
+ if (range_typeid == ANYRANGEOID && !have_anyelement && !have_anymultirange)
{
- /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
+ /*
+ * Special case for ANYRANGE input: okay iff no ANYELEMENT or
+ * ANYMULTIRANGE
+ */
range_typelem = ANYELEMENTOID;
}
else
@@ -1846,6 +1919,71 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(range_typeid),
format_type_be(elem_typeid))));
}
+ else
+ range_typelem = InvalidOid;
+ }
+
+ /* Get the range type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ if (multirange_typeid == ANYMULTIRANGEOID && !have_anyrange && !have_anyelement)
+ {
+ /*
+ * Special case for ANYMULTIRANGE input: okay iff no ANYRANGE or
+ * ANYELEMENT
+ */
+ multirange_typelem = ANYRANGEOID;
+ }
+ else
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+ }
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ /*
+ * Infer the element type too if needed (if there is an ANYMULTIRANGE
+ * and ANYELEMENT, with no ANYRANGE).
+ */
+ if (!OidIsValid(elem_typeid))
+ {
+ elem_typeid = get_range_subtype(range_typeid);
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
}
if (!OidIsValid(elem_typeid))
@@ -1855,6 +1993,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -1927,6 +2066,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -1958,6 +2108,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
@@ -2006,8 +2172,7 @@ resolve_generic_type(Oid declared_type,
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
- context_declared_type == ANYENUMOID ||
- context_declared_type == ANYRANGEOID)
+ context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
@@ -2019,11 +2184,37 @@ resolve_generic_type(Oid declared_type,
format_type_be(context_actual_type))));
return array_typeid;
}
+ else if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid range_typelem = get_range_subtype(context_base_type);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+ Oid range_typelem = get_range_subtype(multirange_typelem);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
- declared_type == ANYENUMOID ||
- declared_type == ANYRANGEOID)
+ declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
@@ -2051,6 +2242,19 @@ resolve_generic_type(Oid declared_type,
"anyrange", format_type_be(context_base_type))));
return range_typelem;
}
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ /* Use the element type corresponding to actual type */
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
@@ -2059,6 +2263,74 @@ resolve_generic_type(Oid declared_type,
return context_actual_type;
}
}
+ else if (declared_type == ANYRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
+ else
+ {
+ /* We can't infer a range from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
+ else if (declared_type == ANYMULTIRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typeid = get_range_multirange(context_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return multirange_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else
+ {
+ /* We can't infer a multirange from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
else
{
/* declared_type isn't polymorphic, so return it as-is */
@@ -2173,6 +2445,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..7118dd0034 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..f1342181a0
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,2171 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/hashutils.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ Oid typiofunc; /* range type's I/O function */
+ Oid typioparam; /* range type's I/O parameter */
+ FmgrInfo proc; /* lookup result for typiofunc */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData * get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks either either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str = NULL;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */ ;
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **) repalloc(ranges,
+ range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(
+ InputFunctionCall(&cache->proc, range_str_copy,
+ cache->typioparam, typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "Unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->proc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(
+ ReceiveFunctionCall(&cache->proc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range = RangeTypePGetDatum(ranges[i]);
+ uint32 range_len;
+ char *range_data;
+
+ range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
+ range_len = VARSIZE(range) - VARHDRSZ;
+ range_data = VARDATA(range);
+
+ pq_sendint32(buf, range_len);
+ pq_sendbytes(buf, range_data, range_len);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &cache->typiofunc);
+
+ if (!OidIsValid(cache->typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(cache->typiofunc, &cache->proc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ RangeType *range;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ bytelen += MAXALIGN(VARSIZE(range));
+ }
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ memcpy(ptr, range, VARSIZE(range));
+ ptr += MAXALIGN(VARSIZE(range));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of any ranges you like into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ * Returns the number of slots actually used,
+ * which may be less than input_range_count but never more.
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType * multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor2(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR, (errmsg("Can't construct multirange with a multi-dimensional array")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL element")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Construct multirange value from a single range.
+ * It'd be nice if we could just use multirange_constructor2
+ * for this case, but we need a non-variadic single-arg function
+ * to let us define a CAST from a range to its multirange.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ RangeType *range;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+
+ range = PG_GETARG_RANGE_P(0);
+
+ /* Make sure the range type matches. */
+ rngtypid = RangeTypeGetOid(range);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 1, &range));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ ereport(ERROR, (errmsg("Zero-param multirange constructor shouldn't have arguments")));
+}
+
+
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+multirange_union(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
+}
+
+/* multirange minus */
+Datum
+multirange_minus(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+multirange_intersect(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+ /*-----------------------------------------------
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1,
+ * but one extra won't hurt.
+ *-----------------------------------------------
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType * mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges1[0], ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1, MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
+ return false;
+
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
+ return false;
+ }
+
+ /* All ranges in mr2 are satisfied */
+ return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 8b26e1a93d..9362995741 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -52,6 +52,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
}
Datum
+binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
+binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_array_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
binary_upgrade_set_next_toast_pg_type_oid(PG_FUNCTION_ARGS)
{
Oid typoid = PG_GETARG_OID(0);
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index ad0e363c70..152e77f44c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -185,6 +186,30 @@ anyrange_out(PG_FUNCTION_ARGS)
}
/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
+/*
* void_in - input routine for pseudo-type VOID.
*
* We allow this so that PL functions can return VOID without any special
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 639e1dad6c..be1e044269 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -431,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -452,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -475,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -485,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -495,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -505,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -515,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -957,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -969,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -993,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1101,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1110,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1132,9 +1180,81 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
+}
+
+/* range, range -> range, range functions */
+
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
+ {
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
+ }
+
+ return false;
}
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
+}
+
+
/* Btree support */
/* btree comparator */
@@ -1144,12 +1264,6 @@ range_cmp(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
- RangeBound lower1,
- lower2;
- RangeBound upper1,
- upper2;
- bool empty1,
- empty2;
int cmp;
check_stack_depth(); /* recurses when subtype is a range type */
@@ -1160,6 +1274,28 @@ range_cmp(PG_FUNCTION_ARGS)
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+ cmp = range_cmp_internal(typcache, r1, r2);
+
+ PG_FREE_IF_COPY(r1, 0);
+ PG_FREE_IF_COPY(r2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
@@ -1177,10 +1313,7 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
- PG_FREE_IF_COPY(r1, 0);
- PG_FREE_IF_COPY(r2, 1);
-
- PG_RETURN_INT32(cmp);
+ return cmp;
}
/* inequality operators using the range_cmp function */
@@ -1218,13 +1351,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1363,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1404,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1433,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1471,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1770,6 +1917,21 @@ range_get_flags(const RangeType *range)
}
/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
+/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
* This is only needed in GiST operations, so we don't include a provision
@@ -1938,6 +2100,45 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
}
/*
+ * Compares two ranges so we can qsort them.
+ * This expects that you give qsort a RangeType **,
+ * so the RangeTypes can be in diverse locations,
+ * as long as you have a list of pointers to them all.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
+/*
* Build an empty range value of the type indicated by the typcache entry.
*/
RangeType *
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 1e3e6d3a7e..2f6257c95f 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2469,6 +2469,16 @@ type_is_range(Oid typid)
}
/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
+/*
* get_type_category_preferred
*
* Given the type OID, fetch its category and preferred-type status.
@@ -3150,6 +3160,58 @@ get_range_subtype(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngmultitypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/* ---------- PG_MULTIRANGE CACHE ---------- */
+
+/*
+ * get_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_multirange_subtype(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 53d9ddf159..fb9179c42a 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -508,6 +508,17 @@ static const struct cachedesc cacheinfo[] = {
},
4
},
+ {RangeRelationId, /* MULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngmultitypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{NamespaceRelationId, /* NAMESPACENAME */
NamespaceNameIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index cdf6331a97..6677bc8bdc 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -278,6 +278,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -294,6 +295,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -500,8 +504,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -538,7 +542,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -563,7 +567,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -588,7 +592,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -640,6 +644,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -684,6 +695,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -759,6 +777,16 @@ lookup_type_cache(Oid type_id, int flags)
}
/*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
+ /*
* If requested, get information about a domain type
*/
if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
@@ -871,6 +899,33 @@ load_rangetype_info(TypeCacheEntry *typentry)
/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Form_pg_range pg_range;
+ HeapTuple tup;
+ Oid rangetypeOid;
+
+ /* get information from pg_range */
+ tup = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(typentry->type_id));
+ /* should not fail, since we already checked typtype ... */
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+ pg_range = (Form_pg_range) GETSTRUCT(tup);
+
+ rangetypeOid = pg_range->rngtypid;
+
+ ReleaseSysCache(tup);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
+
+
+/*
* load_domaintype_info --- helper routine to set up domain constraint info
*
* Note: we assume we're called in a relatively short-lived context, so it's
@@ -1469,11 +1524,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1516,6 +1571,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index b7eee3da1d..defc801f7a 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -470,11 +470,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anynonarray = false;
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
Oid anycollation = InvalidOid;
int i;
@@ -500,12 +502,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
case ANYRANGEOID:
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_anymultirange_result = true;
+ break;
default:
break;
}
}
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/*
@@ -533,6 +538,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (!OidIsValid(anyrange_type))
anyrange_type = get_call_expr_argtype(call_expr, i);
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(anymultirange_type))
+ anymultirange_type = get_call_expr_argtype(call_expr, i);
+ break;
default:
break;
}
@@ -540,7 +549,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/* If nothing found, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -561,19 +570,131 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ anymultirange_type = mltrngtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
/* Enforce ANYNONARRAY if needed */
if (have_anynonarray && type_is_array(anyelement_type))
@@ -640,6 +761,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
default:
break;
}
@@ -664,9 +793,11 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
int inargno;
int i;
@@ -725,6 +856,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ have_anymultirange_result = true;
+ else
+ {
+ if (!OidIsValid(anymultirange_type))
+ {
+ anymultirange_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anymultirange_type))
+ return false;
+ }
+ argtypes[i] = anymultirange_type;
+ }
+ break;
default:
break;
}
@@ -734,12 +880,12 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
/* Done? */
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/* If no input polymorphics, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -760,19 +906,132 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ }
+ else
+ {
+ return false;
+ }
+ }
/* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */
@@ -792,6 +1051,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = anymultirange_type;
+ break;
default:
break;
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 799b6988b7..7237b5e52a 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -274,7 +274,8 @@ static void dumpSearchPath(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type);
+ bool force_array_type,
+ bool include_multirange_type);
static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
static void binary_upgrade_set_pg_class_oids(Archive *fout,
@@ -1583,7 +1584,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4275,15 +4276,49 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
+static Oid
+get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+{
+ /*
+ * If the old version didn't assign an array type, but the new version
+ * does, we must select an unused type OID to assign. This currently only
+ * happens for domains, when upgrading pre-v11 to v11 and up.
+ *
+ * Note: local state here is kind of ugly, but we must have some, since we
+ * mustn't choose the same unused OID more than once.
+ */
+ static Oid next_possible_free_oid = FirstNormalObjectId;
+ PGresult *res;
+ bool is_dup;
+
+ do
+ {
+ ++next_possible_free_oid;
+ printfPQExpBuffer(upgrade_query,
+ "SELECT EXISTS(SELECT 1 "
+ "FROM pg_catalog.pg_type "
+ "WHERE oid = '%u'::pg_catalog.oid);",
+ next_possible_free_oid);
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+ is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
+ PQclear(res);
+ } while (is_dup);
+
+ return next_possible_free_oid;
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type)
+ bool force_array_type,
+ bool include_multirange_type)
{
PQExpBuffer upgrade_query = createPQExpBuffer();
PGresult *res;
Oid pg_type_array_oid;
+ Oid pg_type_multirange_oid;
+ Oid pg_type_multirange_array_oid;
appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
@@ -4304,33 +4339,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQclear(res);
if (!OidIsValid(pg_type_array_oid) && force_array_type)
- {
- /*
- * If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
- *
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
- */
- static Oid next_possible_free_oid = FirstNormalObjectId;
- bool is_dup;
-
- do
- {
- ++next_possible_free_oid;
- printfPQExpBuffer(upgrade_query,
- "SELECT EXISTS(SELECT 1 "
- "FROM pg_catalog.pg_type "
- "WHERE oid = '%u'::pg_catalog.oid);",
- next_possible_free_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
- is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
- PQclear(res);
- } while (is_dup);
-
- pg_type_array_oid = next_possible_free_oid;
- }
+ pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
if (OidIsValid(pg_type_array_oid))
{
@@ -4341,6 +4350,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
pg_type_array_oid);
}
+ /*
+ * Pre-set the multirange type oid and its own array type oid.
+ */
+ if (include_multirange_type)
+ {
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBuffer(upgrade_query,
+ "SELECT t.oid, t.typarray "
+ "FROM pg_catalog.pg_type t "
+ "JOIN pg_catalog.pg_range r "
+ "ON t.oid = r.rngmultitypid "
+ "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
+ pg_type_oid);
+
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+
+ pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+ pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
+
+ PQclear(res);
+ }
+ else
+ {
+ pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ }
+
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_oid);
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_array_oid);
+ }
+
destroyPQExpBuffer(upgrade_query);
}
@@ -4374,7 +4423,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
- pg_type_oid, false);
+ pg_type_oid, false, false);
if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
{
@@ -4923,6 +4972,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
@@ -8037,9 +8091,12 @@ getProcLangs(Archive *fout, int *numProcLangs)
/*
* getCasts
- * get basic information about every cast in the system
+ * get basic information about most casts in the system
*
* numCasts is set to the number of casts read in
+ *
+ * Skip casts from a range to its multirange, since we'll create those
+ * automatically.
*/
CastInfo *
getCasts(Archive *fout, int *numCasts)
@@ -8057,7 +8114,20 @@ getCasts(Archive *fout, int *numCasts)
int i_castcontext;
int i_castmethod;
- if (fout->remoteVersion >= 80400)
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBufferStr(query, "SELECT tableoid, oid, "
+ "castsource, casttarget, castfunc, castcontext, "
+ "castmethod "
+ "FROM pg_cast c "
+ "WHERE NOT EXISTS ( "
+ "SELECT 1 FROM pg_range r "
+ "WHERE c.castsource = r.rngtypid "
+ "AND c.casttarget = r.rngmultitypid "
+ ") "
+ "ORDER BY 3,4");
+ }
+ else if (fout->remoteVersion >= 80400)
{
appendPQExpBufferStr(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
@@ -10245,7 +10315,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
qualtypname);
@@ -10371,7 +10441,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, true);
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
qualtypname);
@@ -10477,7 +10547,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
qualtypname);
@@ -10682,7 +10752,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
@@ -10869,7 +10939,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- true); /* force array type */
+ true, /* force array type */
+ false); /* force multirange type */
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -11057,7 +11128,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
{
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
}
@@ -11331,7 +11402,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
stinfo->baseType->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
fmtQualifiedDumpable(stinfo));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 21004e5078..63e0ba7116 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -175,6 +175,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 0b205a0dc6..3c113fa3bd 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -137,8 +137,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 12d94fe1b3..01d95117cd 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,6 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 13e07fc314..48a6bf5598 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -330,6 +330,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_rngmultitypid_index, 8001, on pg_range using btree(rngmultitypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index ffabe275c0..03bab74253 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..827ac4f03c 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index c67768fcab..0dafd4ec63 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -225,6 +225,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
@@ -385,6 +387,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
@@ -1203,12 +1210,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index 6ef8b8a4e7..14277c8fdf 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -512,4 +512,17 @@
{ castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
castcontext => 'e', castmethod => 'f' },
+# range to multirange
+{ castsource => 'int4range', casttarget => 'int4multirange', castfunc => 'int4multirange(int4range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'int8range', casttarget => 'int8multirange', castfunc => 'int8multirange(int8range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'numrange', casttarget => 'nummultirange', castfunc => 'nummultirange(numrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'daterange', casttarget => 'datemultirange', castfunc => 'datemultirange(daterange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tsrange', casttarget => 'tsmultirange', castfunc => 'tsmultirange(tsrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tstzrange', casttarget => 'tstzmultirange', castfunc => 'tstzmultirange(tstzrange)',
+ castcontext => 'e', castmethod => 'f' },
]
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index 1b81b52df6..f23ba28d9c 100644
--- a/src/include/catalog/pg_cast.h
+++ b/src/include/catalog/pg_cast.h
@@ -23,6 +23,8 @@
#include "catalog/genbki.h"
#include "catalog/pg_cast_d.h"
+#include "catalog/dependency.h"
+
/* ----------------
* pg_cast definition. cpp turns this into
* typedef struct FormData_pg_cast
@@ -87,4 +89,11 @@ typedef enum CoercionMethod
#endif /* EXPOSE_TO_CLIENT_CODE */
+extern ObjectAddress CastCreate(Oid sourcetypeid,
+ Oid targettypeid,
+ Oid funcid,
+ char castcontext,
+ char castmethod,
+ DependencyType behavior);
+
#endif /* PG_CAST_H */
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index ab2f50c9eb..eebebbcdb4 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 7c135da3b1..24f4a32203 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3297,5 +3297,174 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'contsel', oprjoin => 'contjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 26227df216..901fd7b303 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index fcf2a1214c..1f9f61a902 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9624,6 +9624,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9673,6 +9677,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9741,6 +9752,258 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8114',
+ proname => 'multirange_union', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union' },
+{ oid => '8120',
+ proname => 'multirange_minus', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus' },
+{ oid => '8126',
+ proname => 'multirange_intersect', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8146', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 't',
+ prorettype => 'int4multirange', proargtypes => 'int4range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8147', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 't',
+ prorettype => 'nummultirange', proargtypes => 'numrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8148', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 't',
+ prorettype => 'tsmultirange', proargtypes => 'tsrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8149', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 't',
+ prorettype => 'tstzmultirange', proargtypes => 'tstzrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8150', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 't',
+ prorettype => 'datemultirange', proargtypes => 'daterange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8151', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 't',
+ prorettype => 'int8multirange', proargtypes => 'int8range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8152', descr => 'anymultirange cast',
+ proname => 'anymultirange', proisstrict => 't',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
@@ -10123,6 +10386,14 @@
proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
+{ oid => '8144', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
+{ oid => '8145', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
{ oid => '3585', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index 479754c245..10060255c9 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ rngmultitypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', rngmultitypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', rngmultitypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', rngmultitypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ rngmultitypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ rngmultitypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index 98172bb1b6..3efd463c6d 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -45,6 +45,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* subtype difference as a float8, or 0 */
regproc rngsubdiff BKI_LOOKUP(pg_proc);
+
+ /* OID of the range's multirange type */
+ Oid rngmultitypid BKI_LOOKUP(pg_type);
} FormData_pg_range;
/* ----------------
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index fe2c4eabb4..59bae8232f 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -486,6 +486,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -594,5 +628,10 @@
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index e1a5ab3df3..600ac58c9d 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -258,6 +258,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -269,6 +270,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPCATEGORY_ENUM 'E'
#define TYPCATEGORY_GEOMETRIC 'G'
#define TYPCATEGORY_NETWORK 'I' /* think INET */
+#define TYPCATEGORY_MULTIRANGE 'M'
#define TYPCATEGORY_NUMERIC 'N'
#define TYPCATEGORY_PSEUDOTYPE 'P'
#define TYPCATEGORY_RANGE 'R'
@@ -284,7 +286,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#endif /* EXPOSE_TO_CLIENT_CODE */
@@ -343,4 +346,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index fc18d64347..7d0a4a9ea5 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
+extern Oid AssignTypeMultirangeOid(void);
+extern Oid AssignTypeMultirangeArrayOid(void);
extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index a216d6793d..b5ff168160 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -154,6 +154,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -179,6 +180,8 @@ extern void free_attstatsslot(AttStatsSlot *sslot);
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..8779bd1f6e
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ char vl_len_[4]; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern MultirangeType * multirange_minus_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType * multirange_intersect_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType * range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType * make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType * make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 0cbbf09033..204aee4054 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
@@ -92,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -115,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -125,6 +141,7 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
@@ -132,8 +149,12 @@ extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f27b73d76d..bc51bbeb54 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -66,6 +66,7 @@ enum SysCacheIdentifier
INDEXRELID,
LANGNAME,
LANGOID,
+ MULTIRANGETYPE,
NAMESPACENAME,
NAMESPACEOID,
OPERNAMENSP,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index 66ff17dbd5..fe240ea004 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -99,6 +99,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
/*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype;
+
+ /*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
*/
@@ -141,6 +146,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 66bf9f1211..e277329931 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -511,6 +511,8 @@ do_compile(FunctionCallInfo fcinfo,
rettypeid = INT4ARRAYOID;
else if (rettypeid == ANYRANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2494,6 +2496,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYRANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..778699a961 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -141,6 +141,7 @@ owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
owner of type deptest_range
+owner of type deptest_multirange
owner of table deptest2
owner of sequence ss1
owner of type deptest_t
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..4446f8e4c3
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,2386 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+ int4multirange
+----------------
+ {}
+(1 row)
+
+select int4range(1, 3)::int4multirange;
+ int4range
+-----------
+ {[1,3)}
+(1 row)
+
+select int4range(1, null)::int4multirange;
+ int4range
+-----------
+ {[1,)}
+(1 row)
+
+select int4range(null, null)::int4multirange;
+ int4range
+-----------
+ {(,)}
+(1 row)
+
+select 'empty'::textrange::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textrange('a', 'c')::textmultirange;
+ textrange
+-----------
+ {[a,c)}
+(1 row)
+
+select textrange('a', null)::textmultirange;
+ textrange
+-----------
+ {[a,)}
+(1 row)
+
+select textrange(null, null)::textmultirange;
+ textrange
+-----------
+ {(,)}
+(1 row)
+
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT nummultirange() + nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT nummultirange() - nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() * nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select anymultirange($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index c19740e5db..916e1016b5 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -206,6 +213,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -224,6 +233,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -329,13 +340,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
@@ -343,16 +355,19 @@ ORDER BY 2;
2502 | anyarray_recv
2312 | anyelement_in
3504 | anyenum_in
+ 8002 | anymultirange_in
2777 | anynonarray_in
3832 | anyrange_in
750 | array_in
2400 | array_recv
3506 | enum_in
3532 | enum_recv
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(13 rows)
+(16 rows)
-- Look for functions that accept cstring and are neither datatype input
-- functions nor encoding conversion functions. It's almost never a good
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 5ed6ae47ec..4a46a286a1 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1284,7 +1302,7 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
@@ -1395,6 +1413,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1403,6 +1422,16 @@ select * from outparam_succeed(int4range(1,2));
[1,2) | foo
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1411,6 +1440,7 @@ select * from inoutparam_succeed(int4range(1,2));
2 | [1,2)
(1 row)
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
select * from table_succeed(123, int4range(1,11));
@@ -1423,14 +1453,14 @@ select * from table_succeed(123, int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 070de78e85..6f7fcf6326 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
onek|t
onek2|t
path_tbl|f
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index cd7fc03b04..60a9b4d22f 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -193,18 +193,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -238,17 +239,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -316,18 +318,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -361,17 +364,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -629,3 +633,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
+ rngtypid | rngsubtype | rngmultitypid
+----------+------------+---------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index d33a4e143d..5fc0673efa 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index f86f5c5682..98a9afbc7d 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..40f4ef35b0
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,625 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+select int4range(1, 3)::int4multirange;
+select int4range(1, null)::int4multirange;
+select int4range(null, null)::int4multirange;
+select 'empty'::textrange::textmultirange;
+select textrange('a', 'c')::textmultirange;
+select textrange('a', null)::textmultirange;
+select textrange(null, null)::textmultirange;
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT nummultirange() + nummultirange();
+SELECT nummultirange() + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT nummultirange() - nummultirange();
+SELECT nummultirange() - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+SELECT nummultirange() * nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select anymultirange($1), 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 624bea46ce..23932cfef0 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -273,13 +284,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- Look for functions that accept cstring and are neither datatype input
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 2d0ec8964e..4e57352799 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -484,16 +488,25 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fed413bf98..2e34bc8b65 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -151,7 +151,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -180,7 +180,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -232,7 +232,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -261,7 +261,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -465,3 +465,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-01-18 15:19 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Pavel Stehule @ 2020-01-18 15:19 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
pá 17. 1. 2020 v 21:08 odesílatel Paul A Jungwirth <
pj@illuminatedcomputing.com> napsal:
> On Fri, Jan 10, 2020 at 1:38 AM Pavel Stehule <pavel.stehule@gmail.com>
> wrote:
> >> This still leaves the question of how best to format the docs for
> >> these operators. I like being able to combine all the <@ variations
> >> (e.g.) into one table row, but if that is too ugly I could give them
> >> separate rows instead. Giving them all their own row consumes a lot of
> >> vertical space though, and to me that makes the docs more tedious to
> >> read & browse, so it's harder to grasp all the available range-related
> >> operations at a glance.
> >
> >
> > I have similar opinion - maybe is better do documentation for range and
> multirange separately. Sometimes there are still removed operators @+
>
> I like keeping the range/multirange operators together since they are
> so similar for both types, but if others disagree I'd be grateful for
> more feedback.
>
ok
>
> You're right that I left in a few references to the old @+ style
> operators in the examples; I've fixed those.
>
> > If you can share TYPTYPE_RANGE in code for multiranges, then it should
> be 'r'. If not, then it needs own value.
>
> Okay. I think a new 'm' value is warranted because they are not
> interchangeable.
>
> >> I experimented with setting pg_type.typelem to the multirange's range
> >> type, but it seemed to break a lot of things, and reading the code I
> >> saw some places that treat a non-zero typelem as synonymous with being
> >> an array. So I'm reluctant to make this change also, especially when
> >> it is just as easy to query pg_range to get a multirange's range type.
> >
> >
> > ok, it is unhappy, but it is true. This note should be somewhere in
> code, please
>
> I've added a comment about this. I put it at the top of DefineRange
> but let me know if that's the wrong place.
>
> The attached file is also rebased on currrent master.
>
Can be nice to have a polymorphic function
multirange(anymultirange, anyrange) returns anymultirange. This functions
should to do multirange from $2 to type $1
It can enhance to using polymorphic types and simplify casting.
Usage
CREATE OR REPLACE FUNCTION diff(anymultirange, anyrange)
RETURNS anymultirange AS $$
SELECT $1 - multirange($1, $2)
$$ LANGUAGE sql;
when I tried to write this function in plpgsql I got
create or replace function multirange(anymultirange, anyrange) returns
anymultirange as $$
begin
execute format('select $2::%I', pg_typeof($1)) into $1;
return $1;
end;
$$ language plpgsql immutable strict;
ERROR: unrecognized typtype: 109
CONTEXT: compilation of PL/pgSQL function "multirange" near line 1
So probably some support in PL is missing
But all others looks very well
Regards
Pavel
>
> Thanks!
> Paul
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-01-18 16:07 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2020-01-18 16:07 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Sat, Jan 18, 2020 at 7:20 AM Pavel Stehule <pavel.stehule@gmail.com> wrote:
> Can be nice to have a polymorphic function
>
> multirange(anymultirange, anyrange) returns anymultirange. This functions should to do multirange from $2 to type $1
>
> It can enhance to using polymorphic types and simplify casting.
Thanks for taking another look! I actually have that already but it is
named anymultirange:
regression=# select anymultirange(int4range(1,2));
anymultirange
---------------
{[1,2)}
(1 row)
Will that work for you?
I think I only wrote that to satisfy some requirement of having an
anymultirange type, but I agree it could be useful. (I even used it in
the regress tests.) Maybe it's worth documenting too?
> when I tried to write this function in plpgsql I got
>
> create or replace function multirange(anymultirange, anyrange) returns anymultirange as $$
> begin
> execute format('select $2::%I', pg_typeof($1)) into $1;
> return $1;
> end;
> $$ language plpgsql immutable strict;
>
> ERROR: unrecognized typtype: 109
> CONTEXT: compilation of PL/pgSQL function "multirange" near line 1
Hmm, I'll add a test for that and see if I can find the problem.
Thanks!
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-01-18 16:35 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
0 siblings, 1 reply; 152+ messages in thread
From: Pavel Stehule @ 2020-01-18 16:35 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
so 18. 1. 2020 v 17:07 odesílatel Paul A Jungwirth <
pj@illuminatedcomputing.com> napsal:
> On Sat, Jan 18, 2020 at 7:20 AM Pavel Stehule <pavel.stehule@gmail.com>
> wrote:
> > Can be nice to have a polymorphic function
> >
> > multirange(anymultirange, anyrange) returns anymultirange. This
> functions should to do multirange from $2 to type $1
> >
> > It can enhance to using polymorphic types and simplify casting.
>
> Thanks for taking another look! I actually have that already but it is
> named anymultirange:
>
> regression=# select anymultirange(int4range(1,2));
> anymultirange
> ---------------
> {[1,2)}
> (1 row)
>
> Will that work for you?
>
It's better than I though
> I think I only wrote that to satisfy some requirement of having an
> anymultirange type, but I agree it could be useful. (I even used it in
> the regress tests.) Maybe it's worth documenting too?
>
yes
> > when I tried to write this function in plpgsql I got
> >
> > create or replace function multirange(anymultirange, anyrange) returns
> anymultirange as $$
> > begin
> > execute format('select $2::%I', pg_typeof($1)) into $1;
> > return $1;
> > end;
> > $$ language plpgsql immutable strict;
> >
> > ERROR: unrecognized typtype: 109
> > CONTEXT: compilation of PL/pgSQL function "multirange" near line 1
>
> Hmm, I'll add a test for that and see if I can find the problem.
>
ok
> Thanks!
> Paul
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-01-19 08:09 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Pavel Stehule @ 2020-01-19 08:09 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
so 18. 1. 2020 v 17:35 odesílatel Pavel Stehule <pavel.stehule@gmail.com>
napsal:
>
>
> so 18. 1. 2020 v 17:07 odesílatel Paul A Jungwirth <
> pj@illuminatedcomputing.com> napsal:
>
>> On Sat, Jan 18, 2020 at 7:20 AM Pavel Stehule <pavel.stehule@gmail.com>
>> wrote:
>> > Can be nice to have a polymorphic function
>> >
>> > multirange(anymultirange, anyrange) returns anymultirange. This
>> functions should to do multirange from $2 to type $1
>> >
>> > It can enhance to using polymorphic types and simplify casting.
>>
>> Thanks for taking another look! I actually have that already but it is
>> named anymultirange:
>>
>> regression=# select anymultirange(int4range(1,2));
>> anymultirange
>> ---------------
>> {[1,2)}
>> (1 row)
>>
>> Will that work for you?
>>
>
> It's better than I though
>
>
>> I think I only wrote that to satisfy some requirement of having an
>> anymultirange type, but I agree it could be useful. (I even used it in
>> the regress tests.) Maybe it's worth documenting too?
>>
>
Now, I think so name "anymultirange" is not good. Maybe better name is just
"multirange"
> yes
>
>
>> > when I tried to write this function in plpgsql I got
>> >
>> > create or replace function multirange(anymultirange, anyrange) returns
>> anymultirange as $$
>> > begin
>> > execute format('select $2::%I', pg_typeof($1)) into $1;
>> > return $1;
>> > end;
>> > $$ language plpgsql immutable strict;
>> >
>> > ERROR: unrecognized typtype: 109
>> > CONTEXT: compilation of PL/pgSQL function "multirange" near line 1
>>
>> Hmm, I'll add a test for that and see if I can find the problem.
>>
>
> ok
>
>
>> Thanks!
>> Paul
>>
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-01-19 23:34 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2020-01-19 23:34 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Sun, Jan 19, 2020 at 12:10 AM Pavel Stehule <pavel.stehule@gmail.com> wrote:
> Now, I think so name "anymultirange" is not good. Maybe better name is just "multirange"
Are you sure? This function exists to be a cast to an anymultirange,
and I thought the convention was to name cast functions after their
destination type. I can change it, but in my opinion anymultirange
follows the Postgres conventions better. But just let me know and I'll
do "multirange" instead!
Yours,
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-01-20 00:38 ` Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 04:49 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 2 replies; 152+ messages in thread
From: Tom Lane @ 2020-01-20 00:38 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Pavel Stehule <pavel.stehule@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
Paul A Jungwirth <pj@illuminatedcomputing.com> writes:
> On Sun, Jan 19, 2020 at 12:10 AM Pavel Stehule <pavel.stehule@gmail.com> wrote:
>> Now, I think so name "anymultirange" is not good. Maybe better name is just "multirange"
> Are you sure? This function exists to be a cast to an anymultirange,
> and I thought the convention was to name cast functions after their
> destination type.
True for casts involving concrete types, mainly because we'd like
the identity "value::typename == typename(value)" to hold without
too much worry about whether the latter is a plain function call
or a special case. Not sure whether it makes as much sense for
polymorphics, since casting to a polymorphic type is pretty silly:
we do seem to allow you to do that, but it's a no-op.
I'm a little troubled by the notion that what you're talking about
here is not a no-op (if it were, you wouldn't need a function).
That seems like there's something fundamentally not quite right
either with the design or with how you're thinking about it.
As a comparison point, we sometimes describe subscripting as
being a polymorphic operation like
subscript(anyarray, integer) returns anyelement
It would be completely unhelpful to call that anyelement().
I feel like you might be making a similar mistake here.
Alternatively, consider this: a cast from some concrete multirange type
to anymultirange is a no-op, while any other sort of cast probably ought
to be casting to some particular concrete multirange type. That would
line up with the existing operations for plain ranges.
regards, tom lane
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
@ 2020-01-20 04:49 ` Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 0 replies; 152+ messages in thread
From: Pavel Stehule @ 2020-01-20 04:49 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Paul A Jungwirth <pj@illuminatedcomputing.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
po 20. 1. 2020 v 1:38 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
> Paul A Jungwirth <pj@illuminatedcomputing.com> writes:
> > On Sun, Jan 19, 2020 at 12:10 AM Pavel Stehule <pavel.stehule@gmail.com>
> wrote:
> >> Now, I think so name "anymultirange" is not good. Maybe better name is
> just "multirange"
>
> > Are you sure? This function exists to be a cast to an anymultirange,
> > and I thought the convention was to name cast functions after their
> > destination type.
>
> True for casts involving concrete types, mainly because we'd like
> the identity "value::typename == typename(value)" to hold without
> too much worry about whether the latter is a plain function call
> or a special case. Not sure whether it makes as much sense for
> polymorphics, since casting to a polymorphic type is pretty silly:
> we do seem to allow you to do that, but it's a no-op.
>
> I'm a little troubled by the notion that what you're talking about
> here is not a no-op (if it were, you wouldn't need a function).
> That seems like there's something fundamentally not quite right
> either with the design or with how you're thinking about it.
>
I thinking about completeness of operations
I can to write
CREATE OR REPLACE FUNCTION fx(anyarray, anyelement)
RETURNS anyarray AS $$
SELECT $1 || ARRAY[$2]
$$ LANGUAGE sql;
I need to some functionality for moving a value to different category (it
is more generic than casting to specific type (that can hold category)
CREATE OR REPLACE FUNCTION fx(anymultirange, anyrange)
RETURNS anyrage AS $$
SELECT $1 + multirange($1)
$$ LANGUAGE sql;
is just a analogy.
Regards
Pavel
> As a comparison point, we sometimes describe subscripting as
> being a polymorphic operation like
>
> subscript(anyarray, integer) returns anyelement
>
> It would be completely unhelpful to call that anyelement().
> I feel like you might be making a similar mistake here.
>
> Alternatively, consider this: a cast from some concrete multirange type
> to anymultirange is a no-op, while any other sort of cast probably ought
> to be casting to some particular concrete multirange type. That would
> line up with the existing operations for plain ranges.
>
> regards, tom lane
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
@ 2020-01-20 05:57 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
1 sibling, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2020-01-20 05:57 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Pavel Stehule <pavel.stehule@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Sun, Jan 19, 2020 at 4:38 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> True for casts involving concrete types, mainly because we'd like
> the identity "value::typename == typename(value)" to hold without
> too much worry about whether the latter is a plain function call
> or a special case. Not sure whether it makes as much sense for
> polymorphics, since casting to a polymorphic type is pretty silly:
> we do seem to allow you to do that, but it's a no-op.
>
> ...
>
> Alternatively, consider this: a cast from some concrete multirange type
> to anymultirange is a no-op, while any other sort of cast probably ought
> to be casting to some particular concrete multirange type. That would
> line up with the existing operations for plain ranges.
I agree you wouldn't actually cast by saying x::anymultirange, and the
casts we define are already concrete, so instead you'd say
x::int4multirange. But I think having a polymorphic function to
convert from an anyrange to an anymultirange is useful so you can
write generic functions. I can see how calling it "anymultirange" may
be preferring the implementor perspective over the user perspective
though, and how simply "multirange" would be more empathetic. I don't
mind taking that approach.
Yours,
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-01-21 23:54 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-24 07:08 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 2 replies; 152+ messages in thread
From: Paul A Jungwirth @ 2020-01-21 23:54 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Pavel Stehule <pavel.stehule@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Sun, Jan 19, 2020 at 9:57 PM Paul A Jungwirth
<pj@illuminatedcomputing.com> wrote:
> On Sun, Jan 19, 2020 at 4:38 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> > True for casts involving concrete types, mainly because we'd like
> > the identity "value::typename == typename(value)" to hold without
> > too much worry about whether the latter is a plain function call
> > or a special case. Not sure whether it makes as much sense for
> > polymorphics, since casting to a polymorphic type is pretty silly:
> > we do seem to allow you to do that, but it's a no-op.
> >
> > ...
> >
> > Alternatively, consider this: a cast from some concrete multirange type
> > to anymultirange is a no-op, while any other sort of cast probably ought
> > to be casting to some particular concrete multirange type. That would
> > line up with the existing operations for plain ranges.
>
> I agree you wouldn't actually cast by saying x::anymultirange, and the
> casts we define are already concrete, so instead you'd say
> x::int4multirange. But I think having a polymorphic function to
> convert from an anyrange to an anymultirange is useful so you can
> write generic functions. I can see how calling it "anymultirange" may
> be preferring the implementor perspective over the user perspective
> though, and how simply "multirange" would be more empathetic. I don't
> mind taking that approach.
Here is a patch with anymultirange(anyrange) renamed to
multirange(anyrange). I also rebased on the latest master, added
documentation about the multirange(anyrange) function, and slightly
adjusted the formatting of the range functions table.
Thanks,
Paul
Attachments:
[application/octet-stream] v10-multirange.patch (333.2K, ../../CA+renyVOdHap6wvOtG0kQN22Z76ZzjG4PBuf8kVprrJZSz73FA@mail.gmail.com/2-v10-multirange.patch)
download | inline diff:
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index a3046f22d0..7ea2f65778 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -229,9 +229,9 @@
</indexterm>
<para>
- Five pseudo-types of special interest are <type>anyelement</type>,
+ Six pseudo-types of special interest are <type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
- and <type>anyrange</type>,
+ <type>anyrange</type>, and <type>anymultirange</type>.
which are collectively called <firstterm>polymorphic types</firstterm>.
Any function declared using these types is said to be
a <firstterm>polymorphic function</firstterm>. A polymorphic function can
@@ -250,15 +250,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type>, the actual range type in
- the <type>anyrange</type> positions must be a range whose subtype is
- the same type appearing in the <type>anyelement</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -268,6 +268,20 @@
</para>
<para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type>, the actual range type in
+ the <type>anyrange</type> positions must be a range whose subtype is
+ the same type appearing in the <type>anyelement</type> positions.
+ The type <type>anymultirange</type> accepts a multirange
+ whose contents match the other polymorphic types.
+ That is it must hold ranges matching <type>anyrange</type>
+ and base type elements matching <type>anyelement</type> (if either of
+ those are present). If <type>anyarray</type> is present its elements
+ must match the base type of the <type>anyrange</type> and/or the base
+ type of the ranges in an <type>anymultirange</type>.
+ </para>
+
+ <para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
types are allowed. For example, a function declared as
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 6c4359dc7b..687adef230 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -13501,7 +13501,7 @@ SELECT NULLIF(value, '(none)') ...
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -13972,12 +13972,14 @@ NULL baz</literallayout>(3 rows)</entry>
<title>Range Functions and Operators</title>
<para>
- See <xref linkend="rangetypes"/> for an overview of range types.
+ See <xref linkend="rangetypes"/> for an overview of range and multirange types.
</para>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -13995,134 +13997,215 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
- <entry><literal>f</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">f</literallayout></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
- <entry><literal>[5,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)
+'{[5,10)}'::nummultirange + '{[15,20)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,20)
+{[5,10), [15,20)}</literallayout></entry>
</row>
<row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
- <entry><literal>[10,15)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)
+'{[5,15)}'::int8multirange * '{[10,20)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[10,15)
+{[10,15)}</literallayout></entry>
</row>
<row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
- <entry><literal>[5,10)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)
+'{[5,20)}'::int8multirange - '{[10,15)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,10)
+{[5,10), [15,20)}</literallayout></entry>
</row>
</tbody>
@@ -14140,19 +14223,31 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. There are separate operators for union and difference that take
+ multirange parameters and return a multirange, and they do not fail even if
+ their arguments are disjoint. So if you need a union or difference operation
+ for ranges that may be disjoint, you can avoid errors by first casting your
+ ranges to multiranges.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14204,6 +14299,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14215,6 +14321,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>isempty</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14226,6 +14343,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14237,6 +14365,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14248,6 +14387,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14259,6 +14409,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14270,6 +14431,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>range_merge</function>(<type>anyrange</type>, <type>anyrange</type>)
</literal>
</entry>
@@ -14278,16 +14450,38 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
+ <function>multirange</function>(<type>anyrange</type>)
+ </literal>
+ </entry>
+ <entry><type>anymultirange</type></entry>
+ <entry>a multirange containing just the given range</entry>
+ <entry><literal>multirange('[1,2)'::int4range)</literal></entry>
+ <entry><literal>{[1,2)}</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -14609,6 +14803,44 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>string_agg</primary>
</indexterm>
<function>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index b75fb3a392..c8784bdce3 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -232,10 +239,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -269,6 +296,19 @@ SELECT int8range(1, 14, '(]');
SELECT numrange(NULL, 2.2);
</programlisting>
</para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
+</programlisting>
+ </para>
</sect2>
<sect2 id="rangetypes-discrete">
@@ -342,6 +382,11 @@ SELECT '[1.234, 5.678]'::floatrange;
</para>
<para>
+ When you define your own range you automatically get a corresponding
+ multirange type.
+ </para>
+
+ <para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
ordering that determines which values fall into a given range.
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index d5da81c801..eedd7eadb3 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -25,6 +25,7 @@ OBJS = \
objectaddress.o \
partition.o \
pg_aggregate.o \
+ pg_cast.o \
pg_collation.o \
pg_constraint.o \
pg_conversion.o \
diff --git a/src/backend/catalog/pg_cast.c b/src/backend/catalog/pg_cast.c
new file mode 100644
index 0000000000..50b44e3a96
--- /dev/null
+++ b/src/backend/catalog/pg_cast.c
@@ -0,0 +1,116 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_cast.c
+ * routines to support manipulation of the pg_cast relation
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_cast.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/pg_cast.h"
+#include "access/table.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "tcop/tcopprot.h"
+#include "utils/builtins.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+/*
+ * ----------------------------------------------------------------
+ * CastCreate
+ * ----------------------------------------------------------------
+ */
+ObjectAddress
+CastCreate(Oid sourcetypeid, Oid targettypeid, Oid funcid, char castcontext,
+ char castmethod, DependencyType behavior)
+{
+ Relation relation;
+ HeapTuple tuple;
+ Oid castid;
+ Datum values[Natts_pg_cast];
+ bool nulls[Natts_pg_cast];
+ ObjectAddress myself,
+ referenced;
+
+ relation = table_open(CastRelationId, RowExclusiveLock);
+
+ /*
+ * Check for duplicate. This is just to give a friendly error message,
+ * the unique index would catch it anyway (so no need to sweat about race
+ * conditions).
+ */
+ tuple = SearchSysCache2(CASTSOURCETARGET,
+ ObjectIdGetDatum(sourcetypeid),
+ ObjectIdGetDatum(targettypeid));
+ if (HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("cast from type %s to type %s already exists",
+ format_type_be(sourcetypeid),
+ format_type_be(targettypeid))));
+
+ /* ready to go */
+ castid = GetNewOidWithIndex(relation, CastOidIndexId, Anum_pg_cast_oid);
+ values[Anum_pg_cast_oid - 1] = ObjectIdGetDatum(castid);
+ values[Anum_pg_cast_castsource - 1] = ObjectIdGetDatum(sourcetypeid);
+ values[Anum_pg_cast_casttarget - 1] = ObjectIdGetDatum(targettypeid);
+ values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid);
+ values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext);
+ values[Anum_pg_cast_castmethod - 1] = CharGetDatum(castmethod);
+
+ MemSet(nulls, false, sizeof(nulls));
+
+ tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
+
+ CatalogTupleInsert(relation, tuple);
+
+ /* make dependency entries */
+ myself.classId = CastRelationId;
+ myself.objectId = castid;
+ myself.objectSubId = 0;
+
+ /* dependency on source type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = sourcetypeid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, behavior);
+
+ /* dependency on target type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = targettypeid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, behavior);
+
+ /* dependency on function */
+ if (OidIsValid(funcid))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = funcid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, behavior);
+ }
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ /* Post creation hook for new cast */
+ InvokeObjectPostCreateHook(CastRelationId, castid, 0);
+
+ heap_freetuple(tuple);
+
+ table_close(relation, RowExclusiveLock);
+
+ return myself;
+}
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 5194dcaac0..6efcac52a0 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -192,6 +192,7 @@ ProcedureCreate(const char *procedureName,
genericInParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericInParam = true;
anyrangeInParam = true;
break;
@@ -219,6 +220,7 @@ ProcedureCreate(const char *procedureName,
genericOutParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericOutParam = true;
anyrangeOutParam = true;
break;
@@ -231,10 +233,10 @@ ProcedureCreate(const char *procedureName,
/*
* Do not allow polymorphic return type unless at least one input argument
- * is polymorphic. ANYRANGE return type is even stricter: must have an
- * ANYRANGE input (since we can't deduce the specific range type from
- * ANYELEMENT). Also, do not allow return type INTERNAL unless at least
- * one input argument is INTERNAL.
+ * is polymorphic. ANYRANGE and ANYMULTIRANGE return types are even
+ * stricter: must have an ANYRANGE or ANYMULTIRANGE input (since we can't
+ * deduce the specific range type from ANYELEMENT). Also, do not allow
+ * return type INTERNAL unless at least one input argument is INTERNAL.
*/
if ((IsPolymorphicType(returnType) || genericOutParam)
&& !genericInParam)
@@ -243,12 +245,13 @@ ProcedureCreate(const char *procedureName,
errmsg("cannot determine result data type"),
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
- if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
- !anyrangeInParam)
+ if ((returnType == ANYRANGEOID ||
+ returnType == ANYMULTIRANGEOID ||
+ anyrangeOutParam) && !anyrangeInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
- errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
+ errdetail("A function returning \"anyrange\" or \"anymultirange\" must have at least one \"anyrange\" or \"anymultirange\" argument.")));
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
ereport(ERROR,
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index b5bc36c2bd..1217a6ddd0 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,12 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 8d7572da51..d74697ef9c 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -27,6 +27,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
@@ -36,6 +37,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
+ bool tryOriginal);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -783,31 +787,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
char *
makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
- char *arr = (char *) palloc(NAMEDATALEN);
- int namelen = strlen(typeName);
- int i;
-
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
+ char *arr;
- if (i >= NAMEDATALEN - 1)
+ arr = makeUniqueTypeName(typeName, typeNamespace, false);
+ if (arr == NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -878,3 +861,91 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char *buf;
+ char *mrname;
+ char *rangestr;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "_multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ char *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
+
+ buf = psprintf("%s%s%s", prefix, "multi", rangestr);
+ }
+ else
+ buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
+
+ /* clip it at NAMEDATALEN-1 bytes */
+ buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
+
+ mrname = makeUniqueTypeName(buf, typeNamespace, true);
+ if (mrname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mrname;
+}
+
+/*
+ * makeUniqueTypeName
+ * Generate a unique name for a prospective new type
+ *
+ * Given a typeName, return a new palloc'ed name by preprending underscores
+ * until a non-conflicting name results.
+ *
+ * If tryOriginal, first try with zero underscores.
+ */
+static char *
+makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
+{
+ int i;
+ int namelen;
+ char dest[NAMEDATALEN];
+
+ Assert(strlen(typeName) <= NAMEDATALEN - 1);
+
+ if (tryOriginal &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(typeName);
+
+ /*
+ * The idea is to prepend underscores as needed until we make a name that
+ * doesn't collide with anything ...
+ */
+ namelen = strlen(typeName);
+ for (i = 1; i < NAMEDATALEN - 1; i++)
+ {
+ dest[i - 1] = '_';
+ strlcpy(dest + i, typeName, NAMEDATALEN - i);
+ if (namelen + i >= NAMEDATALEN)
+ truncate_identifier(dest, NAMEDATALEN, false);
+
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(dest);
+ }
+
+ return NULL;
+}
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index c31c57e5e9..732487ff8d 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -1497,17 +1497,12 @@ CreateCast(CreateCastStmt *stmt)
char sourcetyptype;
char targettyptype;
Oid funcid;
- Oid castid;
int nargs;
char castcontext;
char castmethod;
- Relation relation;
HeapTuple tuple;
- Datum values[Natts_pg_cast];
- bool nulls[Natts_pg_cast];
- ObjectAddress myself,
- referenced;
AclResult aclresult;
+ ObjectAddress myself;
sourcetypeid = typenameTypeId(NULL, stmt->sourcetype);
targettypeid = typenameTypeId(NULL, stmt->targettype);
@@ -1731,74 +1726,8 @@ CreateCast(CreateCastStmt *stmt)
break;
}
- relation = table_open(CastRelationId, RowExclusiveLock);
-
- /*
- * Check for duplicate. This is just to give a friendly error message,
- * the unique index would catch it anyway (so no need to sweat about race
- * conditions).
- */
- tuple = SearchSysCache2(CASTSOURCETARGET,
- ObjectIdGetDatum(sourcetypeid),
- ObjectIdGetDatum(targettypeid));
- if (HeapTupleIsValid(tuple))
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_OBJECT),
- errmsg("cast from type %s to type %s already exists",
- format_type_be(sourcetypeid),
- format_type_be(targettypeid))));
-
- /* ready to go */
- castid = GetNewOidWithIndex(relation, CastOidIndexId, Anum_pg_cast_oid);
- values[Anum_pg_cast_oid - 1] = ObjectIdGetDatum(castid);
- values[Anum_pg_cast_castsource - 1] = ObjectIdGetDatum(sourcetypeid);
- values[Anum_pg_cast_casttarget - 1] = ObjectIdGetDatum(targettypeid);
- values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid);
- values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext);
- values[Anum_pg_cast_castmethod - 1] = CharGetDatum(castmethod);
-
- MemSet(nulls, false, sizeof(nulls));
-
- tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
-
- CatalogTupleInsert(relation, tuple);
-
- /* make dependency entries */
- myself.classId = CastRelationId;
- myself.objectId = castid;
- myself.objectSubId = 0;
-
- /* dependency on source type */
- referenced.classId = TypeRelationId;
- referenced.objectId = sourcetypeid;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-
- /* dependency on target type */
- referenced.classId = TypeRelationId;
- referenced.objectId = targettypeid;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-
- /* dependency on function */
- if (OidIsValid(funcid))
- {
- referenced.classId = ProcedureRelationId;
- referenced.objectId = funcid;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
- }
-
- /* dependency on extension */
- recordDependencyOnCurrentExtension(&myself, false);
-
- /* Post creation hook for new cast */
- InvokeObjectPostCreateHook(CastRelationId, castid, 0);
-
- heap_freetuple(tuple);
-
- table_close(relation, RowExclusiveLock);
-
+ myself = CastCreate(sourcetypeid, targettypeid, funcid, castcontext,
+ castmethod, DEPENDENCY_NORMAL);
return myself;
}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 52097363fd..a8dea8d58e 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -42,6 +42,7 @@
#include "catalog/objectaccess.h"
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
@@ -85,9 +86,14 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid,
+ Oid rangeArrayOid, Oid *castFuncOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -811,7 +817,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1346,6 +1353,11 @@ checkEnumOwner(HeapTuple tup)
/*
* DefineRange
* Registers a new range type.
+ *
+ * Perhaps it might be worthwhile to set pg_type.typelem to the base type,
+ * and likewise on multiranges to set it to the range type. But having a
+ * non-zero typelem is treated elsewhere as a synonym for being an array,
+ * and users might have queries with that same assumption.
*/
ObjectAddress
DefineRange(CreateRangeStmt *stmt)
@@ -1354,7 +1366,11 @@ DefineRange(CreateRangeStmt *stmt)
Oid typeNamespace;
Oid typoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1371,6 +1387,8 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
+ Oid castFuncOid;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1519,8 +1537,10 @@ DefineRange(CreateRangeStmt *stmt)
/* alignment must be 'i' or 'd' for ranges */
alignment = (subtypalign == 'd') ? 'd' : 'i';
- /* Allocate OID for array type */
+ /* Allocate OID for array type, its multirange, and its multirange array */
rangeArrayOid = AssignTypeArrayOid();
+ multirangeOid = AssignTypeMultirangeOid();
+ multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
address =
@@ -1557,9 +1577,46 @@ DefineRange(CreateRangeStmt *stmt)
InvalidOid); /* type's collation (ranges never have one) */
Assert(typoid == address.objectId);
+ /* Create the multirange that goes with it */
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(multirangeOid, /* force assignment of this type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_MULTIRANGE, /* type-category (multirange type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ Assert(multirangeOid == mltrngaddress.objectId);
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
@@ -1600,8 +1657,53 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ multirangeOid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ multirangeOid, typoid, rangeArrayOid,
+ &castFuncOid);
+
+ /* Create cast from the range type to its multirange type */
+ CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1679,6 +1781,147 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ *
+ * Sets castFuncOid to the oid of the new constructor that can be used
+ * to cast from a range to a multirange.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
+ Oid *castFuncOid)
+{
+ ObjectAddress myself,
+ referenced;
+ oidvector *argtypes;
+ Datum allParamTypes;
+ ArrayType *allParameterTypes;
+ Datum paramModes;
+ ArrayType *parameterModes;
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ /* 0-arg constructor - for empty multiranges */
+ argtypes = buildoidvector(NULL, 0);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor0", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /*
+ * Make the constructor internally-dependent on the multirange type so
+ * that they go away silently when the type is dropped. Note that pg_dump
+ * depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+
+ /*
+ * 1-arg constructor - for casts
+ *
+ * In theory we shouldn't need both this and the vararg (n-arg) constructor,
+ * but having a separate 1-arg function lets us define casts against it.
+ */
+ argtypes = buildoidvector(&rangeOid, 1);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor1", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ true, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ *castFuncOid = myself.objectId;
+
+ /* n-arg constructor - vararg */
+ argtypes = buildoidvector(&rangeArrayOid, 1);
+ allParamTypes = ObjectIdGetDatum(rangeArrayOid);
+ allParameterTypes = construct_array(&allParamTypes,
+ 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
+ parameterModes = construct_array(¶mModes, 1, CHAROID,
+ 1, true, 'c');
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor2", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(allParameterTypes), /* allParameterTypes */
+ PointerGetDatum(parameterModes), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ pfree(allParameterTypes);
+ pfree(parameterModes);
+}
/*
* Find suitable I/O functions for a type.
@@ -2104,6 +2347,72 @@ AssignTypeArrayOid(void)
return type_array_oid;
}
+/*
+ * AssignTypeMultirangeOid
+ *
+ * Pre-assign the range type's multirange OID for use in pg_type.oid
+ */
+Oid
+AssignTypeMultirangeOid(void)
+{
+ Oid type_multirange_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
+
+ type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
+ binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_oid;
+}
+
+/*
+ * AssignTypeMultirangeArrayOid
+ *
+ * Pre-assign the range type's multirange array OID for use in pg_type.typarray
+ */
+Oid
+AssignTypeMultirangeArrayOid(void)
+{
+ Oid type_multirange_array_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
+
+ type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
+ binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_array_oid;
+}
+
/*-------------------------------------------------------------------
* DefineCompositeType
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 929f758ef4..47d17f43a4 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -187,18 +187,20 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
- targetTypeId == ANYRANGEOID)
+ targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call anyarray_in,
- * anyenum_in, or anyrange_in, which will produce an error. Also, if
- * what we have is a domain over array, enum, or range, we have to
- * relabel it to its base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * anyarray_in, anyenum_in, anyrange_in, or anymultirange_in, which
+ * will produce an error. Also, if what we have is a domain over
+ * array, enum, range, or multirange, we have to relabel it to its
+ * base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1481,6 +1483,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid array_typelem;
Oid range_typeid = InvalidOid;
Oid range_typelem;
+ Oid multirange_typeid = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
@@ -1527,6 +1531,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1577,6 +1590,39 @@ check_generic_type_consistency(const Oid *actual_arg_types,
/* otherwise, they better match */
return false;
}
+ else
+ range_typelem = InvalidOid; /* keep compiler quiet */
+ }
+
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
}
if (have_anynonarray)
@@ -1680,13 +1726,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid array_typelem;
- Oid range_typelem;
+ Oid range_typelem = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
+ bool have_anyrange = (rettype == ANYRANGEOID);
+ bool have_anymultirange = (rettype == ANYMULTIRANGEOID);
/*
* Loop through the arguments to see if we have any that are polymorphic.
@@ -1744,7 +1794,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else if (decl_type == ANYRANGEOID)
{
- have_generics = true;
+ have_generics = have_anyrange = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
@@ -1762,6 +1812,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ have_generics = have_anymultirange = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
}
/*
@@ -1812,9 +1882,12 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- if (range_typeid == ANYRANGEOID && !have_anyelement)
+ if (range_typeid == ANYRANGEOID && !have_anyelement && !have_anymultirange)
{
- /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
+ /*
+ * Special case for ANYRANGE input: okay iff no ANYELEMENT or
+ * ANYMULTIRANGE
+ */
range_typelem = ANYELEMENTOID;
}
else
@@ -1846,6 +1919,71 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(range_typeid),
format_type_be(elem_typeid))));
}
+ else
+ range_typelem = InvalidOid;
+ }
+
+ /* Get the range type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ if (multirange_typeid == ANYMULTIRANGEOID && !have_anyrange && !have_anyelement)
+ {
+ /*
+ * Special case for ANYMULTIRANGE input: okay iff no ANYRANGE or
+ * ANYELEMENT
+ */
+ multirange_typelem = ANYRANGEOID;
+ }
+ else
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+ }
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ /*
+ * Infer the element type too if needed (if there is an ANYMULTIRANGE
+ * and ANYELEMENT, with no ANYRANGE).
+ */
+ if (!OidIsValid(elem_typeid))
+ {
+ elem_typeid = get_range_subtype(range_typeid);
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
}
if (!OidIsValid(elem_typeid))
@@ -1855,6 +1993,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -1927,6 +2066,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -1958,6 +2108,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
@@ -2006,8 +2172,7 @@ resolve_generic_type(Oid declared_type,
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
- context_declared_type == ANYENUMOID ||
- context_declared_type == ANYRANGEOID)
+ context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
@@ -2019,11 +2184,37 @@ resolve_generic_type(Oid declared_type,
format_type_be(context_actual_type))));
return array_typeid;
}
+ else if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid range_typelem = get_range_subtype(context_base_type);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+ Oid range_typelem = get_range_subtype(multirange_typelem);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
- declared_type == ANYENUMOID ||
- declared_type == ANYRANGEOID)
+ declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
@@ -2051,6 +2242,19 @@ resolve_generic_type(Oid declared_type,
"anyrange", format_type_be(context_base_type))));
return range_typelem;
}
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ /* Use the element type corresponding to actual type */
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
@@ -2059,6 +2263,74 @@ resolve_generic_type(Oid declared_type,
return context_actual_type;
}
}
+ else if (declared_type == ANYRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
+ else
+ {
+ /* We can't infer a range from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
+ else if (declared_type == ANYMULTIRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typeid = get_range_multirange(context_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return multirange_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else
+ {
+ /* We can't infer a multirange from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
else
{
/* declared_type isn't polymorphic, so return it as-is */
@@ -2173,6 +2445,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..7118dd0034 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..f1342181a0
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,2171 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/hashutils.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ Oid typiofunc; /* range type's I/O function */
+ Oid typioparam; /* range type's I/O parameter */
+ FmgrInfo proc; /* lookup result for typiofunc */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData * get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks either either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str = NULL;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */ ;
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **) repalloc(ranges,
+ range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(
+ InputFunctionCall(&cache->proc, range_str_copy,
+ cache->typioparam, typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "Unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->proc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(
+ ReceiveFunctionCall(&cache->proc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range = RangeTypePGetDatum(ranges[i]);
+ uint32 range_len;
+ char *range_data;
+
+ range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
+ range_len = VARSIZE(range) - VARHDRSZ;
+ range_data = VARDATA(range);
+
+ pq_sendint32(buf, range_len);
+ pq_sendbytes(buf, range_data, range_len);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &cache->typiofunc);
+
+ if (!OidIsValid(cache->typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(cache->typiofunc, &cache->proc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ RangeType *range;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ bytelen += MAXALIGN(VARSIZE(range));
+ }
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ memcpy(ptr, range, VARSIZE(range));
+ ptr += MAXALIGN(VARSIZE(range));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of any ranges you like into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ * Returns the number of slots actually used,
+ * which may be less than input_range_count but never more.
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType * multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor2(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR, (errmsg("Can't construct multirange with a multi-dimensional array")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL element")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Construct multirange value from a single range.
+ * It'd be nice if we could just use multirange_constructor2
+ * for this case, but we need a non-variadic single-arg function
+ * to let us define a CAST from a range to its multirange.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ RangeType *range;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+
+ range = PG_GETARG_RANGE_P(0);
+
+ /* Make sure the range type matches. */
+ rngtypid = RangeTypeGetOid(range);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 1, &range));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ ereport(ERROR, (errmsg("Zero-param multirange constructor shouldn't have arguments")));
+}
+
+
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+multirange_union(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
+}
+
+/* multirange minus */
+Datum
+multirange_minus(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+multirange_intersect(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+ /*-----------------------------------------------
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1,
+ * but one extra won't hurt.
+ *-----------------------------------------------
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType * mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges1[0], ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1, MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
+ return false;
+
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
+ return false;
+ }
+
+ /* All ranges in mr2 are satisfied */
+ return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 8b26e1a93d..9362995741 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -52,6 +52,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
}
Datum
+binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
+binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_array_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
binary_upgrade_set_next_toast_pg_type_oid(PG_FUNCTION_ARGS)
{
Oid typoid = PG_GETARG_OID(0);
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index ad0e363c70..152e77f44c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -185,6 +186,30 @@ anyrange_out(PG_FUNCTION_ARGS)
}
/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
+/*
* void_in - input routine for pseudo-type VOID.
*
* We allow this so that PL functions can return VOID without any special
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 639e1dad6c..be1e044269 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -431,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -452,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -475,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -485,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -495,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -505,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -515,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -957,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -969,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -993,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1101,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1110,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1132,9 +1180,81 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
+}
+
+/* range, range -> range, range functions */
+
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
+ {
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
+ }
+
+ return false;
}
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
+}
+
+
/* Btree support */
/* btree comparator */
@@ -1144,12 +1264,6 @@ range_cmp(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
- RangeBound lower1,
- lower2;
- RangeBound upper1,
- upper2;
- bool empty1,
- empty2;
int cmp;
check_stack_depth(); /* recurses when subtype is a range type */
@@ -1160,6 +1274,28 @@ range_cmp(PG_FUNCTION_ARGS)
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+ cmp = range_cmp_internal(typcache, r1, r2);
+
+ PG_FREE_IF_COPY(r1, 0);
+ PG_FREE_IF_COPY(r2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
@@ -1177,10 +1313,7 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
- PG_FREE_IF_COPY(r1, 0);
- PG_FREE_IF_COPY(r2, 1);
-
- PG_RETURN_INT32(cmp);
+ return cmp;
}
/* inequality operators using the range_cmp function */
@@ -1218,13 +1351,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1363,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1404,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1433,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1471,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1770,6 +1917,21 @@ range_get_flags(const RangeType *range)
}
/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
+/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
* This is only needed in GiST operations, so we don't include a provision
@@ -1938,6 +2100,45 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
}
/*
+ * Compares two ranges so we can qsort them.
+ * This expects that you give qsort a RangeType **,
+ * so the RangeTypes can be in diverse locations,
+ * as long as you have a list of pointers to them all.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
+/*
* Build an empty range value of the type indicated by the typcache entry.
*/
RangeType *
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 1e3e6d3a7e..2f6257c95f 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2469,6 +2469,16 @@ type_is_range(Oid typid)
}
/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
+/*
* get_type_category_preferred
*
* Given the type OID, fetch its category and preferred-type status.
@@ -3150,6 +3160,58 @@ get_range_subtype(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngmultitypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/* ---------- PG_MULTIRANGE CACHE ---------- */
+
+/*
+ * get_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_multirange_subtype(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 53d9ddf159..fb9179c42a 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -508,6 +508,17 @@ static const struct cachedesc cacheinfo[] = {
},
4
},
+ {RangeRelationId, /* MULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngmultitypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{NamespaceRelationId, /* NAMESPACENAME */
NamespaceNameIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index cdf6331a97..6677bc8bdc 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -278,6 +278,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -294,6 +295,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -500,8 +504,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -538,7 +542,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -563,7 +567,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -588,7 +592,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -640,6 +644,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -684,6 +695,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -759,6 +777,16 @@ lookup_type_cache(Oid type_id, int flags)
}
/*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
+ /*
* If requested, get information about a domain type
*/
if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
@@ -871,6 +899,33 @@ load_rangetype_info(TypeCacheEntry *typentry)
/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Form_pg_range pg_range;
+ HeapTuple tup;
+ Oid rangetypeOid;
+
+ /* get information from pg_range */
+ tup = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(typentry->type_id));
+ /* should not fail, since we already checked typtype ... */
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+ pg_range = (Form_pg_range) GETSTRUCT(tup);
+
+ rangetypeOid = pg_range->rngtypid;
+
+ ReleaseSysCache(tup);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
+
+
+/*
* load_domaintype_info --- helper routine to set up domain constraint info
*
* Note: we assume we're called in a relatively short-lived context, so it's
@@ -1469,11 +1524,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1516,6 +1571,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index b7eee3da1d..defc801f7a 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -470,11 +470,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anynonarray = false;
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
Oid anycollation = InvalidOid;
int i;
@@ -500,12 +502,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
case ANYRANGEOID:
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_anymultirange_result = true;
+ break;
default:
break;
}
}
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/*
@@ -533,6 +538,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (!OidIsValid(anyrange_type))
anyrange_type = get_call_expr_argtype(call_expr, i);
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(anymultirange_type))
+ anymultirange_type = get_call_expr_argtype(call_expr, i);
+ break;
default:
break;
}
@@ -540,7 +549,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/* If nothing found, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -561,19 +570,131 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ anymultirange_type = mltrngtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
/* Enforce ANYNONARRAY if needed */
if (have_anynonarray && type_is_array(anyelement_type))
@@ -640,6 +761,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
default:
break;
}
@@ -664,9 +793,11 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
int inargno;
int i;
@@ -725,6 +856,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ have_anymultirange_result = true;
+ else
+ {
+ if (!OidIsValid(anymultirange_type))
+ {
+ anymultirange_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anymultirange_type))
+ return false;
+ }
+ argtypes[i] = anymultirange_type;
+ }
+ break;
default:
break;
}
@@ -734,12 +880,12 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
/* Done? */
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/* If no input polymorphics, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -760,19 +906,132 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ }
+ else
+ {
+ return false;
+ }
+ }
/* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */
@@ -792,6 +1051,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = anymultirange_type;
+ break;
default:
break;
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 799b6988b7..7237b5e52a 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -274,7 +274,8 @@ static void dumpSearchPath(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type);
+ bool force_array_type,
+ bool include_multirange_type);
static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
static void binary_upgrade_set_pg_class_oids(Archive *fout,
@@ -1583,7 +1584,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4275,15 +4276,49 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
+static Oid
+get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+{
+ /*
+ * If the old version didn't assign an array type, but the new version
+ * does, we must select an unused type OID to assign. This currently only
+ * happens for domains, when upgrading pre-v11 to v11 and up.
+ *
+ * Note: local state here is kind of ugly, but we must have some, since we
+ * mustn't choose the same unused OID more than once.
+ */
+ static Oid next_possible_free_oid = FirstNormalObjectId;
+ PGresult *res;
+ bool is_dup;
+
+ do
+ {
+ ++next_possible_free_oid;
+ printfPQExpBuffer(upgrade_query,
+ "SELECT EXISTS(SELECT 1 "
+ "FROM pg_catalog.pg_type "
+ "WHERE oid = '%u'::pg_catalog.oid);",
+ next_possible_free_oid);
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+ is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
+ PQclear(res);
+ } while (is_dup);
+
+ return next_possible_free_oid;
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type)
+ bool force_array_type,
+ bool include_multirange_type)
{
PQExpBuffer upgrade_query = createPQExpBuffer();
PGresult *res;
Oid pg_type_array_oid;
+ Oid pg_type_multirange_oid;
+ Oid pg_type_multirange_array_oid;
appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
@@ -4304,33 +4339,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQclear(res);
if (!OidIsValid(pg_type_array_oid) && force_array_type)
- {
- /*
- * If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
- *
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
- */
- static Oid next_possible_free_oid = FirstNormalObjectId;
- bool is_dup;
-
- do
- {
- ++next_possible_free_oid;
- printfPQExpBuffer(upgrade_query,
- "SELECT EXISTS(SELECT 1 "
- "FROM pg_catalog.pg_type "
- "WHERE oid = '%u'::pg_catalog.oid);",
- next_possible_free_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
- is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
- PQclear(res);
- } while (is_dup);
-
- pg_type_array_oid = next_possible_free_oid;
- }
+ pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
if (OidIsValid(pg_type_array_oid))
{
@@ -4341,6 +4350,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
pg_type_array_oid);
}
+ /*
+ * Pre-set the multirange type oid and its own array type oid.
+ */
+ if (include_multirange_type)
+ {
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBuffer(upgrade_query,
+ "SELECT t.oid, t.typarray "
+ "FROM pg_catalog.pg_type t "
+ "JOIN pg_catalog.pg_range r "
+ "ON t.oid = r.rngmultitypid "
+ "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
+ pg_type_oid);
+
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+
+ pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+ pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
+
+ PQclear(res);
+ }
+ else
+ {
+ pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ }
+
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_oid);
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_array_oid);
+ }
+
destroyPQExpBuffer(upgrade_query);
}
@@ -4374,7 +4423,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
- pg_type_oid, false);
+ pg_type_oid, false, false);
if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
{
@@ -4923,6 +4972,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
@@ -8037,9 +8091,12 @@ getProcLangs(Archive *fout, int *numProcLangs)
/*
* getCasts
- * get basic information about every cast in the system
+ * get basic information about most casts in the system
*
* numCasts is set to the number of casts read in
+ *
+ * Skip casts from a range to its multirange, since we'll create those
+ * automatically.
*/
CastInfo *
getCasts(Archive *fout, int *numCasts)
@@ -8057,7 +8114,20 @@ getCasts(Archive *fout, int *numCasts)
int i_castcontext;
int i_castmethod;
- if (fout->remoteVersion >= 80400)
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBufferStr(query, "SELECT tableoid, oid, "
+ "castsource, casttarget, castfunc, castcontext, "
+ "castmethod "
+ "FROM pg_cast c "
+ "WHERE NOT EXISTS ( "
+ "SELECT 1 FROM pg_range r "
+ "WHERE c.castsource = r.rngtypid "
+ "AND c.casttarget = r.rngmultitypid "
+ ") "
+ "ORDER BY 3,4");
+ }
+ else if (fout->remoteVersion >= 80400)
{
appendPQExpBufferStr(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
@@ -10245,7 +10315,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
qualtypname);
@@ -10371,7 +10441,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, true);
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
qualtypname);
@@ -10477,7 +10547,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
qualtypname);
@@ -10682,7 +10752,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
@@ -10869,7 +10939,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- true); /* force array type */
+ true, /* force array type */
+ false); /* force multirange type */
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -11057,7 +11128,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
{
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
}
@@ -11331,7 +11402,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
stinfo->baseType->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
fmtQualifiedDumpable(stinfo));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 21004e5078..63e0ba7116 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -175,6 +175,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 0b205a0dc6..3c113fa3bd 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -137,8 +137,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 12d94fe1b3..01d95117cd 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,6 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 13e07fc314..48a6bf5598 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -330,6 +330,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_rngmultitypid_index, 8001, on pg_range using btree(rngmultitypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index ffabe275c0..03bab74253 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..827ac4f03c 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index c67768fcab..0dafd4ec63 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -225,6 +225,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
@@ -385,6 +387,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
@@ -1203,12 +1210,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index 6ef8b8a4e7..14277c8fdf 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -512,4 +512,17 @@
{ castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
castcontext => 'e', castmethod => 'f' },
+# range to multirange
+{ castsource => 'int4range', casttarget => 'int4multirange', castfunc => 'int4multirange(int4range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'int8range', casttarget => 'int8multirange', castfunc => 'int8multirange(int8range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'numrange', casttarget => 'nummultirange', castfunc => 'nummultirange(numrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'daterange', casttarget => 'datemultirange', castfunc => 'datemultirange(daterange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tsrange', casttarget => 'tsmultirange', castfunc => 'tsmultirange(tsrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tstzrange', casttarget => 'tstzmultirange', castfunc => 'tstzmultirange(tstzrange)',
+ castcontext => 'e', castmethod => 'f' },
]
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index 1b81b52df6..f23ba28d9c 100644
--- a/src/include/catalog/pg_cast.h
+++ b/src/include/catalog/pg_cast.h
@@ -23,6 +23,8 @@
#include "catalog/genbki.h"
#include "catalog/pg_cast_d.h"
+#include "catalog/dependency.h"
+
/* ----------------
* pg_cast definition. cpp turns this into
* typedef struct FormData_pg_cast
@@ -87,4 +89,11 @@ typedef enum CoercionMethod
#endif /* EXPOSE_TO_CLIENT_CODE */
+extern ObjectAddress CastCreate(Oid sourcetypeid,
+ Oid targettypeid,
+ Oid funcid,
+ char castcontext,
+ char castmethod,
+ DependencyType behavior);
+
#endif /* PG_CAST_H */
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index ab2f50c9eb..eebebbcdb4 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 7c135da3b1..24f4a32203 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3297,5 +3297,174 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'contsel', oprjoin => 'contjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 26227df216..901fd7b303 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index fcf2a1214c..2c1bb373be 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9624,6 +9624,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9673,6 +9677,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9741,6 +9752,258 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8114',
+ proname => 'multirange_union', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union' },
+{ oid => '8120',
+ proname => 'multirange_minus', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus' },
+{ oid => '8126',
+ proname => 'multirange_intersect', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8146', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 't',
+ prorettype => 'int4multirange', proargtypes => 'int4range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8147', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 't',
+ prorettype => 'nummultirange', proargtypes => 'numrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8148', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 't',
+ prorettype => 'tsmultirange', proargtypes => 'tsrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8149', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 't',
+ prorettype => 'tstzmultirange', proargtypes => 'tstzrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8150', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 't',
+ prorettype => 'datemultirange', proargtypes => 'daterange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8151', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 't',
+ prorettype => 'int8multirange', proargtypes => 'int8range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8152', descr => 'anymultirange cast',
+ proname => 'multirange', proisstrict => 't',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
@@ -10123,6 +10386,14 @@
proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
+{ oid => '8144', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
+{ oid => '8145', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
{ oid => '3585', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index 479754c245..10060255c9 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ rngmultitypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', rngmultitypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', rngmultitypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', rngmultitypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ rngmultitypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ rngmultitypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index 98172bb1b6..3efd463c6d 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -45,6 +45,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* subtype difference as a float8, or 0 */
regproc rngsubdiff BKI_LOOKUP(pg_proc);
+
+ /* OID of the range's multirange type */
+ Oid rngmultitypid BKI_LOOKUP(pg_type);
} FormData_pg_range;
/* ----------------
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index fe2c4eabb4..59bae8232f 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -486,6 +486,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -594,5 +628,10 @@
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index e1a5ab3df3..600ac58c9d 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -258,6 +258,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -269,6 +270,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPCATEGORY_ENUM 'E'
#define TYPCATEGORY_GEOMETRIC 'G'
#define TYPCATEGORY_NETWORK 'I' /* think INET */
+#define TYPCATEGORY_MULTIRANGE 'M'
#define TYPCATEGORY_NUMERIC 'N'
#define TYPCATEGORY_PSEUDOTYPE 'P'
#define TYPCATEGORY_RANGE 'R'
@@ -284,7 +286,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#endif /* EXPOSE_TO_CLIENT_CODE */
@@ -343,4 +346,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index fc18d64347..7d0a4a9ea5 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
+extern Oid AssignTypeMultirangeOid(void);
+extern Oid AssignTypeMultirangeArrayOid(void);
extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index a216d6793d..b5ff168160 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -154,6 +154,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -179,6 +180,8 @@ extern void free_attstatsslot(AttStatsSlot *sslot);
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..8779bd1f6e
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ char vl_len_[4]; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern MultirangeType * multirange_minus_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType * multirange_intersect_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType * range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType * make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType * make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 0cbbf09033..204aee4054 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
@@ -92,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -115,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -125,6 +141,7 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
@@ -132,8 +149,12 @@ extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f27b73d76d..bc51bbeb54 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -66,6 +66,7 @@ enum SysCacheIdentifier
INDEXRELID,
LANGNAME,
LANGOID,
+ MULTIRANGETYPE,
NAMESPACENAME,
NAMESPACEOID,
OPERNAMENSP,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index 66ff17dbd5..fe240ea004 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -99,6 +99,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
/*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype;
+
+ /*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
*/
@@ -141,6 +146,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 66bf9f1211..f61dc7e69a 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -511,6 +511,8 @@ do_compile(FunctionCallInfo fcinfo,
rettypeid = INT4ARRAYOID;
else if (rettypeid == ANYRANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2087,6 +2089,7 @@ build_datatype(HeapTuple typeTup, int32 typmod,
case TYPTYPE_BASE:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
+ case TYPTYPE_MULTIRANGE:
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_COMPOSITE:
@@ -2494,6 +2497,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYRANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..778699a961 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -141,6 +141,7 @@ owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
owner of type deptest_range
+owner of type deptest_multirange
owner of table deptest2
owner of sequence ss1
owner of type deptest_t
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..723f2408b0
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,2395 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+ int4multirange
+----------------
+ {}
+(1 row)
+
+select int4range(1, 3)::int4multirange;
+ int4range
+-----------
+ {[1,3)}
+(1 row)
+
+select int4range(1, null)::int4multirange;
+ int4range
+-----------
+ {[1,)}
+(1 row)
+
+select int4range(null, null)::int4multirange;
+ int4range
+-----------
+ {(,)}
+(1 row)
+
+select 'empty'::textrange::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textrange('a', 'c')::textmultirange;
+ textrange
+-----------
+ {[a,c)}
+(1 row)
+
+select textrange('a', null)::textmultirange;
+ textrange
+-----------
+ {[a,)}
+(1 row)
+
+select textrange(null, null)::textmultirange;
+ textrange
+-----------
+ {(,)}
+(1 row)
+
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT nummultirange() + nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT nummultirange() - nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() * nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+ mr_polymorphic
+----------------
+ {[1,4)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index c19740e5db..916e1016b5 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -206,6 +213,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -224,6 +233,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -329,13 +340,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
@@ -343,16 +355,19 @@ ORDER BY 2;
2502 | anyarray_recv
2312 | anyelement_in
3504 | anyenum_in
+ 8002 | anymultirange_in
2777 | anynonarray_in
3832 | anyrange_in
750 | array_in
2400 | array_recv
3506 | enum_in
3532 | enum_recv
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(13 rows)
+(16 rows)
-- Look for functions that accept cstring and are neither datatype input
-- functions nor encoding conversion functions. It's almost never a good
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 5ed6ae47ec..4a46a286a1 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1284,7 +1302,7 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
@@ -1395,6 +1413,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1403,6 +1422,16 @@ select * from outparam_succeed(int4range(1,2));
[1,2) | foo
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1411,6 +1440,7 @@ select * from inoutparam_succeed(int4range(1,2));
2 | [1,2)
(1 row)
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
select * from table_succeed(123, int4range(1,11));
@@ -1423,14 +1453,14 @@ select * from table_succeed(123, int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 070de78e85..6f7fcf6326 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
onek|t
onek2|t
path_tbl|f
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index cd7fc03b04..60a9b4d22f 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -193,18 +193,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -238,17 +239,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -316,18 +318,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -361,17 +364,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -629,3 +633,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
+ rngtypid | rngsubtype | rngmultitypid
+----------+------------+---------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index d33a4e143d..5fc0673efa 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index f86f5c5682..98a9afbc7d 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..0f70919e1a
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,630 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+select int4range(1, 3)::int4multirange;
+select int4range(1, null)::int4multirange;
+select int4range(null, null)::int4multirange;
+select 'empty'::textrange::textmultirange;
+select textrange('a', 'c')::textmultirange;
+select textrange('a', null)::textmultirange;
+select textrange(null, null)::textmultirange;
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT nummultirange() + nummultirange();
+SELECT nummultirange() + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT nummultirange() - nummultirange();
+SELECT nummultirange() - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+SELECT nummultirange() * nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 624bea46ce..23932cfef0 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -273,13 +284,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- Look for functions that accept cstring and are neither datatype input
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 2d0ec8964e..4e57352799 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -484,16 +488,25 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fed413bf98..2e34bc8b65 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -151,7 +151,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -180,7 +180,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -232,7 +232,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -261,7 +261,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -465,3 +465,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-01-24 07:08 ` Pavel Stehule <pavel.stehule@gmail.com>
1 sibling, 0 replies; 152+ messages in thread
From: Pavel Stehule @ 2020-01-24 07:08 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
Hi
st 22. 1. 2020 v 0:55 odesílatel Paul A Jungwirth <
pj@illuminatedcomputing.com> napsal:
> On Sun, Jan 19, 2020 at 9:57 PM Paul A Jungwirth
> <pj@illuminatedcomputing.com> wrote:
> > On Sun, Jan 19, 2020 at 4:38 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> > > True for casts involving concrete types, mainly because we'd like
> > > the identity "value::typename == typename(value)" to hold without
> > > too much worry about whether the latter is a plain function call
> > > or a special case. Not sure whether it makes as much sense for
> > > polymorphics, since casting to a polymorphic type is pretty silly:
> > > we do seem to allow you to do that, but it's a no-op.
> > >
> > > ...
> > >
> > > Alternatively, consider this: a cast from some concrete multirange type
> > > to anymultirange is a no-op, while any other sort of cast probably
> ought
> > > to be casting to some particular concrete multirange type. That would
> > > line up with the existing operations for plain ranges.
> >
> > I agree you wouldn't actually cast by saying x::anymultirange, and the
> > casts we define are already concrete, so instead you'd say
> > x::int4multirange. But I think having a polymorphic function to
> > convert from an anyrange to an anymultirange is useful so you can
> > write generic functions. I can see how calling it "anymultirange" may
> > be preferring the implementor perspective over the user perspective
> > though, and how simply "multirange" would be more empathetic. I don't
> > mind taking that approach.
>
> Here is a patch with anymultirange(anyrange) renamed to
> multirange(anyrange). I also rebased on the latest master, added
> documentation about the multirange(anyrange) function, and slightly
> adjusted the formatting of the range functions table.
>
I think so this patch is ready for commiter.
All tests passed, the doc is good enough (the chapter name "Range functions
and Operators" should be renamed to "Range/multirange functions and
Operators"
The code formatting and comments looks well
Thank you for your work
Regards
Pavel
> Thanks,
> Paul
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-03-04 02:28 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-04 21:33 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-09 19:59 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
1 sibling, 3 replies; 152+ messages in thread
From: Alvaro Herrera @ 2020-03-04 02:28 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
I started to review this again. I'm down to figuring out whether the
typecache changes make sense; in doing so I realized that the syscaches
weren't perfectly defined (I think leftovers from when there was a
pg_multirange catalog, earlier in development), so I fixed that.
0001 is mostly Paul's v10 patch, rebased to current master; no
conflicts, I had to make a couple of small other adjustments to catch up
with current times.
The other patches are fairly obvious; changes in 0004 are described in
its commit message.
I'll continue to try to think through the typecache aspects of this
patch.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
[text/x-diff] v11-0001-Paul-s-v10.patch (337.8K, ../../20200304022849.GA5402@alvherre.pgsql/2-v11-0001-Paul-s-v10.patch)
download | inline diff:
From 866c738f328be0adc3b1481ce0a5871d589110ea Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Tue, 11 Feb 2020 18:10:30 -0300
Subject: [PATCH v11 1/4] Paul's v10
---
doc/src/sgml/extend.sgml | 28 +-
doc/src/sgml/func.sgml | 326 ++-
doc/src/sgml/rangetypes.sgml | 47 +-
src/backend/catalog/Makefile | 1 +
src/backend/catalog/pg_cast.c | 116 +
src/backend/catalog/pg_proc.c | 17 +-
src/backend/catalog/pg_range.c | 10 +-
src/backend/catalog/pg_type.c | 119 +-
src/backend/commands/functioncmds.c | 77 +-
src/backend/commands/typecmds.c | 315 ++-
src/backend/parser/parse_coerce.c | 307 ++-
src/backend/utils/adt/Makefile | 1 +
src/backend/utils/adt/multirangetypes.c | 2170 +++++++++++++++
src/backend/utils/adt/pg_upgrade_support.c | 22 +
src/backend/utils/adt/pseudotypes.c | 25 +
src/backend/utils/adt/rangetypes.c | 349 ++-
src/backend/utils/cache/lsyscache.c | 62 +
src/backend/utils/cache/syscache.c | 11 +
src/backend/utils/cache/typcache.c | 109 +-
src/backend/utils/fmgr/funcapi.c | 290 +-
src/bin/pg_dump/pg_dump.c | 151 +-
src/bin/pg_dump/pg_dump.h | 1 +
src/include/access/tupmacs.h | 4 +-
src/include/catalog/binary_upgrade.h | 2 +
src/include/catalog/indexing.h | 3 +
src/include/catalog/pg_aggregate.dat | 11 +
src/include/catalog/pg_amop.dat | 22 +
src/include/catalog/pg_amproc.dat | 12 +-
src/include/catalog/pg_cast.dat | 13 +
src/include/catalog/pg_cast.h | 9 +
src/include/catalog/pg_opclass.dat | 4 +
src/include/catalog/pg_operator.dat | 169 ++
src/include/catalog/pg_opfamily.dat | 4 +
src/include/catalog/pg_proc.dat | 271 ++
src/include/catalog/pg_range.dat | 15 +-
src/include/catalog/pg_range.h | 5 +-
src/include/catalog/pg_type.dat | 39 +
src/include/catalog/pg_type.h | 8 +-
src/include/commands/typecmds.h | 2 +
src/include/utils/lsyscache.h | 3 +
src/include/utils/multirangetypes.h | 103 +
src/include/utils/rangetypes.h | 29 +-
src/include/utils/syscache.h | 1 +
src/include/utils/typcache.h | 6 +
src/pl/plpgsql/src/pl_comp.c | 6 +
src/test/regress/expected/dependency.out | 1 +
src/test/regress/expected/hash_func.out | 13 +
src/test/regress/expected/multirangetypes.out | 2395 +++++++++++++++++
src/test/regress/expected/opr_sanity.out | 26 +-
src/test/regress/expected/rangetypes.out | 38 +-
src/test/regress/expected/sanity_check.out | 2 +
src/test/regress/expected/type_sanity.out | 46 +-
src/test/regress/parallel_schedule | 3 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/hash_func.sql | 10 +
src/test/regress/sql/multirangetypes.sql | 630 +++++
src/test/regress/sql/opr_sanity.sql | 18 +-
src/test/regress/sql/rangetypes.sql | 13 +
src/test/regress/sql/type_sanity.sql | 16 +-
59 files changed, 8142 insertions(+), 365 deletions(-)
create mode 100644 src/backend/catalog/pg_cast.c
create mode 100644 src/backend/utils/adt/multirangetypes.c
create mode 100644 src/include/utils/multirangetypes.h
create mode 100644 src/test/regress/expected/multirangetypes.out
create mode 100644 src/test/regress/sql/multirangetypes.sql
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 9ec1af780b..92a1a254ac 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -229,9 +229,9 @@
</indexterm>
<para>
- Five pseudo-types of special interest are <type>anyelement</type>,
+ Six pseudo-types of special interest are <type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
- and <type>anyrange</type>,
+ <type>anyrange</type>, and <type>anymultirange</type>.
which are collectively called <firstterm>polymorphic types</firstterm>.
Any function declared using these types is said to be
a <firstterm>polymorphic function</firstterm>. A polymorphic function can
@@ -250,15 +250,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type>, the actual range type in
- the <type>anyrange</type> positions must be a range whose subtype is
- the same type appearing in the <type>anyelement</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -267,6 +267,20 @@
be an enum type.
</para>
+ <para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type>, the actual range type in
+ the <type>anyrange</type> positions must be a range whose subtype is
+ the same type appearing in the <type>anyelement</type> positions.
+ The type <type>anymultirange</type> accepts a multirange
+ whose contents match the other polymorphic types.
+ That is it must hold ranges matching <type>anyrange</type>
+ and base type elements matching <type>anyelement</type> (if either of
+ those are present). If <type>anyarray</type> is present its elements
+ must match the base type of the <type>anyrange</type> and/or the base
+ type of the ranges in an <type>anymultirange</type>.
+ </para>
+
<para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 28035f1635..fc26c3fb14 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -13535,7 +13535,7 @@ SELECT NULLIF(value, '(none)') ...
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14006,12 +14006,14 @@ NULL baz</literallayout>(3 rows)</entry>
<title>Range Functions and Operators</title>
<para>
- See <xref linkend="rangetypes"/> for an overview of range types.
+ See <xref linkend="rangetypes"/> for an overview of range and multirange types.
</para>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14029,134 +14031,215 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
- <entry><literal>f</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">f</literallayout></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
- <entry><literal>[5,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)
+'{[5,10)}'::nummultirange + '{[15,20)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,20)
+{[5,10), [15,20)}</literallayout></entry>
</row>
<row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
- <entry><literal>[10,15)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)
+'{[5,15)}'::int8multirange * '{[10,20)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[10,15)
+{[10,15)}</literallayout></entry>
</row>
<row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
- <entry><literal>[5,10)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)
+'{[5,20)}'::int8multirange - '{[10,15)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,10)
+{[5,10), [15,20)}</literallayout></entry>
</row>
</tbody>
@@ -14174,19 +14257,31 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. There are separate operators for union and difference that take
+ multirange parameters and return a multirange, and they do not fail even if
+ their arguments are disjoint. So if you need a union or difference operation
+ for ranges that may be disjoint, you can avoid errors by first casting your
+ ranges to multiranges.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14235,6 +14330,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower(numrange(1.1,2.2))</literal></entry>
<entry><literal>1.1</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14246,6 +14352,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper(numrange(1.1,2.2))</literal></entry>
<entry><literal>2.2</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14257,6 +14374,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>isempty(numrange(1.1,2.2))</literal></entry>
<entry><literal>false</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14268,6 +14396,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower_inc(numrange(1.1,2.2))</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14279,6 +14418,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper_inc(numrange(1.1,2.2))</literal></entry>
<entry><literal>false</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14290,6 +14440,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower_inf('(,)'::daterange)</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14301,6 +14462,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper_inf('(,)'::daterange)</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14312,16 +14484,38 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
+ <function>multirange</function>(<type>anyrange</type>)
+ </literal>
+ </entry>
+ <entry><type>anymultirange</type></entry>
+ <entry>a multirange containing just the given range</entry>
+ <entry><literal>multirange('[1,2)'::int4range)</literal></entry>
+ <entry><literal>{[1,2)}</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -14640,6 +14834,44 @@ NULL baz</literallayout>(3 rows)</entry>
</entry>
</row>
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
<row>
<entry>
<indexterm>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index b75fb3a392..c8784bdce3 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -232,10 +239,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -267,6 +294,19 @@ SELECT int8range(1, 14, '(]');
-- Using NULL for either bound causes the range to be unbounded on that side.
SELECT numrange(NULL, 2.2);
+</programlisting>
+ </para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
</programlisting>
</para>
</sect2>
@@ -341,6 +381,11 @@ SELECT '[1.234, 5.678]'::floatrange;
function in this example.
</para>
+ <para>
+ When you define your own range you automatically get a corresponding
+ multirange type.
+ </para>
+
<para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index f8f0b4841c..9499bb33e5 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -25,6 +25,7 @@ OBJS = \
objectaddress.o \
partition.o \
pg_aggregate.o \
+ pg_cast.o \
pg_collation.o \
pg_constraint.o \
pg_conversion.o \
diff --git a/src/backend/catalog/pg_cast.c b/src/backend/catalog/pg_cast.c
new file mode 100644
index 0000000000..50b44e3a96
--- /dev/null
+++ b/src/backend/catalog/pg_cast.c
@@ -0,0 +1,116 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_cast.c
+ * routines to support manipulation of the pg_cast relation
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_cast.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/pg_cast.h"
+#include "access/table.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "tcop/tcopprot.h"
+#include "utils/builtins.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+/*
+ * ----------------------------------------------------------------
+ * CastCreate
+ * ----------------------------------------------------------------
+ */
+ObjectAddress
+CastCreate(Oid sourcetypeid, Oid targettypeid, Oid funcid, char castcontext,
+ char castmethod, DependencyType behavior)
+{
+ Relation relation;
+ HeapTuple tuple;
+ Oid castid;
+ Datum values[Natts_pg_cast];
+ bool nulls[Natts_pg_cast];
+ ObjectAddress myself,
+ referenced;
+
+ relation = table_open(CastRelationId, RowExclusiveLock);
+
+ /*
+ * Check for duplicate. This is just to give a friendly error message,
+ * the unique index would catch it anyway (so no need to sweat about race
+ * conditions).
+ */
+ tuple = SearchSysCache2(CASTSOURCETARGET,
+ ObjectIdGetDatum(sourcetypeid),
+ ObjectIdGetDatum(targettypeid));
+ if (HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("cast from type %s to type %s already exists",
+ format_type_be(sourcetypeid),
+ format_type_be(targettypeid))));
+
+ /* ready to go */
+ castid = GetNewOidWithIndex(relation, CastOidIndexId, Anum_pg_cast_oid);
+ values[Anum_pg_cast_oid - 1] = ObjectIdGetDatum(castid);
+ values[Anum_pg_cast_castsource - 1] = ObjectIdGetDatum(sourcetypeid);
+ values[Anum_pg_cast_casttarget - 1] = ObjectIdGetDatum(targettypeid);
+ values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid);
+ values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext);
+ values[Anum_pg_cast_castmethod - 1] = CharGetDatum(castmethod);
+
+ MemSet(nulls, false, sizeof(nulls));
+
+ tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
+
+ CatalogTupleInsert(relation, tuple);
+
+ /* make dependency entries */
+ myself.classId = CastRelationId;
+ myself.objectId = castid;
+ myself.objectSubId = 0;
+
+ /* dependency on source type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = sourcetypeid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, behavior);
+
+ /* dependency on target type */
+ referenced.classId = TypeRelationId;
+ referenced.objectId = targettypeid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, behavior);
+
+ /* dependency on function */
+ if (OidIsValid(funcid))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = funcid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, behavior);
+ }
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, false);
+
+ /* Post creation hook for new cast */
+ InvokeObjectPostCreateHook(CastRelationId, castid, 0);
+
+ heap_freetuple(tuple);
+
+ table_close(relation, RowExclusiveLock);
+
+ return myself;
+}
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 5194dcaac0..6efcac52a0 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -192,6 +192,7 @@ ProcedureCreate(const char *procedureName,
genericInParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericInParam = true;
anyrangeInParam = true;
break;
@@ -219,6 +220,7 @@ ProcedureCreate(const char *procedureName,
genericOutParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericOutParam = true;
anyrangeOutParam = true;
break;
@@ -231,10 +233,10 @@ ProcedureCreate(const char *procedureName,
/*
* Do not allow polymorphic return type unless at least one input argument
- * is polymorphic. ANYRANGE return type is even stricter: must have an
- * ANYRANGE input (since we can't deduce the specific range type from
- * ANYELEMENT). Also, do not allow return type INTERNAL unless at least
- * one input argument is INTERNAL.
+ * is polymorphic. ANYRANGE and ANYMULTIRANGE return types are even
+ * stricter: must have an ANYRANGE or ANYMULTIRANGE input (since we can't
+ * deduce the specific range type from ANYELEMENT). Also, do not allow
+ * return type INTERNAL unless at least one input argument is INTERNAL.
*/
if ((IsPolymorphicType(returnType) || genericOutParam)
&& !genericInParam)
@@ -243,12 +245,13 @@ ProcedureCreate(const char *procedureName,
errmsg("cannot determine result data type"),
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
- if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
- !anyrangeInParam)
+ if ((returnType == ANYRANGEOID ||
+ returnType == ANYMULTIRANGEOID ||
+ anyrangeOutParam) && !anyrangeInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
- errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
+ errdetail("A function returning \"anyrange\" or \"anymultirange\" must have at least one \"anyrange\" or \"anymultirange\" argument.")));
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
ereport(ERROR,
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index b5bc36c2bd..1217a6ddd0 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,12 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 8d7572da51..d74697ef9c 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -27,6 +27,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
@@ -36,6 +37,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
+ bool tryOriginal);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -783,31 +787,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
char *
makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
- char *arr = (char *) palloc(NAMEDATALEN);
- int namelen = strlen(typeName);
- int i;
+ char *arr;
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
-
- if (i >= NAMEDATALEN - 1)
+ arr = makeUniqueTypeName(typeName, typeNamespace, false);
+ if (arr == NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -878,3 +861,91 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char *buf;
+ char *mrname;
+ char *rangestr;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "_multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ char *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
+
+ buf = psprintf("%s%s%s", prefix, "multi", rangestr);
+ }
+ else
+ buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
+
+ /* clip it at NAMEDATALEN-1 bytes */
+ buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
+
+ mrname = makeUniqueTypeName(buf, typeNamespace, true);
+ if (mrname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mrname;
+}
+
+/*
+ * makeUniqueTypeName
+ * Generate a unique name for a prospective new type
+ *
+ * Given a typeName, return a new palloc'ed name by preprending underscores
+ * until a non-conflicting name results.
+ *
+ * If tryOriginal, first try with zero underscores.
+ */
+static char *
+makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
+{
+ int i;
+ int namelen;
+ char dest[NAMEDATALEN];
+
+ Assert(strlen(typeName) <= NAMEDATALEN - 1);
+
+ if (tryOriginal &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(typeName);
+
+ /*
+ * The idea is to prepend underscores as needed until we make a name that
+ * doesn't collide with anything ...
+ */
+ namelen = strlen(typeName);
+ for (i = 1; i < NAMEDATALEN - 1; i++)
+ {
+ dest[i - 1] = '_';
+ strlcpy(dest + i, typeName, NAMEDATALEN - i);
+ if (namelen + i >= NAMEDATALEN)
+ truncate_identifier(dest, NAMEDATALEN, false);
+
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(dest);
+ }
+
+ return NULL;
+}
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 540044b2d6..3c047fa5e1 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -1498,17 +1498,12 @@ CreateCast(CreateCastStmt *stmt)
char sourcetyptype;
char targettyptype;
Oid funcid;
- Oid castid;
int nargs;
char castcontext;
char castmethod;
- Relation relation;
HeapTuple tuple;
- Datum values[Natts_pg_cast];
- bool nulls[Natts_pg_cast];
- ObjectAddress myself,
- referenced;
AclResult aclresult;
+ ObjectAddress myself;
sourcetypeid = typenameTypeId(NULL, stmt->sourcetype);
targettypeid = typenameTypeId(NULL, stmt->targettype);
@@ -1732,74 +1727,8 @@ CreateCast(CreateCastStmt *stmt)
break;
}
- relation = table_open(CastRelationId, RowExclusiveLock);
-
- /*
- * Check for duplicate. This is just to give a friendly error message,
- * the unique index would catch it anyway (so no need to sweat about race
- * conditions).
- */
- tuple = SearchSysCache2(CASTSOURCETARGET,
- ObjectIdGetDatum(sourcetypeid),
- ObjectIdGetDatum(targettypeid));
- if (HeapTupleIsValid(tuple))
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_OBJECT),
- errmsg("cast from type %s to type %s already exists",
- format_type_be(sourcetypeid),
- format_type_be(targettypeid))));
-
- /* ready to go */
- castid = GetNewOidWithIndex(relation, CastOidIndexId, Anum_pg_cast_oid);
- values[Anum_pg_cast_oid - 1] = ObjectIdGetDatum(castid);
- values[Anum_pg_cast_castsource - 1] = ObjectIdGetDatum(sourcetypeid);
- values[Anum_pg_cast_casttarget - 1] = ObjectIdGetDatum(targettypeid);
- values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid);
- values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext);
- values[Anum_pg_cast_castmethod - 1] = CharGetDatum(castmethod);
-
- MemSet(nulls, false, sizeof(nulls));
-
- tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
-
- CatalogTupleInsert(relation, tuple);
-
- /* make dependency entries */
- myself.classId = CastRelationId;
- myself.objectId = castid;
- myself.objectSubId = 0;
-
- /* dependency on source type */
- referenced.classId = TypeRelationId;
- referenced.objectId = sourcetypeid;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-
- /* dependency on target type */
- referenced.classId = TypeRelationId;
- referenced.objectId = targettypeid;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
-
- /* dependency on function */
- if (OidIsValid(funcid))
- {
- referenced.classId = ProcedureRelationId;
- referenced.objectId = funcid;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
- }
-
- /* dependency on extension */
- recordDependencyOnCurrentExtension(&myself, false);
-
- /* Post creation hook for new cast */
- InvokeObjectPostCreateHook(CastRelationId, castid, 0);
-
- heap_freetuple(tuple);
-
- table_close(relation, RowExclusiveLock);
-
+ myself = CastCreate(sourcetypeid, targettypeid, funcid, castcontext,
+ castmethod, DEPENDENCY_NORMAL);
return myself;
}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 52097363fd..a8dea8d58e 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -42,6 +42,7 @@
#include "catalog/objectaccess.h"
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
@@ -85,9 +86,14 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid,
+ Oid rangeArrayOid, Oid *castFuncOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -811,7 +817,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1346,6 +1353,11 @@ checkEnumOwner(HeapTuple tup)
/*
* DefineRange
* Registers a new range type.
+ *
+ * Perhaps it might be worthwhile to set pg_type.typelem to the base type,
+ * and likewise on multiranges to set it to the range type. But having a
+ * non-zero typelem is treated elsewhere as a synonym for being an array,
+ * and users might have queries with that same assumption.
*/
ObjectAddress
DefineRange(CreateRangeStmt *stmt)
@@ -1354,7 +1366,11 @@ DefineRange(CreateRangeStmt *stmt)
Oid typeNamespace;
Oid typoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1371,6 +1387,8 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
+ Oid castFuncOid;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1519,8 +1537,10 @@ DefineRange(CreateRangeStmt *stmt)
/* alignment must be 'i' or 'd' for ranges */
alignment = (subtypalign == 'd') ? 'd' : 'i';
- /* Allocate OID for array type */
+ /* Allocate OID for array type, its multirange, and its multirange array */
rangeArrayOid = AssignTypeArrayOid();
+ multirangeOid = AssignTypeMultirangeOid();
+ multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
address =
@@ -1557,9 +1577,46 @@ DefineRange(CreateRangeStmt *stmt)
InvalidOid); /* type's collation (ranges never have one) */
Assert(typoid == address.objectId);
+ /* Create the multirange that goes with it */
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(multirangeOid, /* force assignment of this type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_MULTIRANGE, /* type-category (multirange type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ Assert(multirangeOid == mltrngaddress.objectId);
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
@@ -1600,8 +1657,53 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ multirangeOid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ multirangeOid, typoid, rangeArrayOid,
+ &castFuncOid);
+
+ /* Create cast from the range type to its multirange type */
+ CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1679,6 +1781,147 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ *
+ * Sets castFuncOid to the oid of the new constructor that can be used
+ * to cast from a range to a multirange.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
+ Oid *castFuncOid)
+{
+ ObjectAddress myself,
+ referenced;
+ oidvector *argtypes;
+ Datum allParamTypes;
+ ArrayType *allParameterTypes;
+ Datum paramModes;
+ ArrayType *parameterModes;
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ /* 0-arg constructor - for empty multiranges */
+ argtypes = buildoidvector(NULL, 0);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor0", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /*
+ * Make the constructor internally-dependent on the multirange type so
+ * that they go away silently when the type is dropped. Note that pg_dump
+ * depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+
+ /*
+ * 1-arg constructor - for casts
+ *
+ * In theory we shouldn't need both this and the vararg (n-arg) constructor,
+ * but having a separate 1-arg function lets us define casts against it.
+ */
+ argtypes = buildoidvector(&rangeOid, 1);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor1", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ true, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ *castFuncOid = myself.objectId;
+
+ /* n-arg constructor - vararg */
+ argtypes = buildoidvector(&rangeArrayOid, 1);
+ allParamTypes = ObjectIdGetDatum(rangeArrayOid);
+ allParameterTypes = construct_array(&allParamTypes,
+ 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
+ parameterModes = construct_array(¶mModes, 1, CHAROID,
+ 1, true, 'c');
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor2", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(allParameterTypes), /* allParameterTypes */
+ PointerGetDatum(parameterModes), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ pfree(allParameterTypes);
+ pfree(parameterModes);
+}
/*
* Find suitable I/O functions for a type.
@@ -2104,6 +2347,72 @@ AssignTypeArrayOid(void)
return type_array_oid;
}
+/*
+ * AssignTypeMultirangeOid
+ *
+ * Pre-assign the range type's multirange OID for use in pg_type.oid
+ */
+Oid
+AssignTypeMultirangeOid(void)
+{
+ Oid type_multirange_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
+
+ type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
+ binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_oid;
+}
+
+/*
+ * AssignTypeMultirangeArrayOid
+ *
+ * Pre-assign the range type's multirange array OID for use in pg_type.typarray
+ */
+Oid
+AssignTypeMultirangeArrayOid(void)
+{
+ Oid type_multirange_array_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
+
+ type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
+ binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_array_oid;
+}
+
/*-------------------------------------------------------------------
* DefineCompositeType
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 929f758ef4..47d17f43a4 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -187,18 +187,20 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
- targetTypeId == ANYRANGEOID)
+ targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call anyarray_in,
- * anyenum_in, or anyrange_in, which will produce an error. Also, if
- * what we have is a domain over array, enum, or range, we have to
- * relabel it to its base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * anyarray_in, anyenum_in, anyrange_in, or anymultirange_in, which
+ * will produce an error. Also, if what we have is a domain over
+ * array, enum, range, or multirange, we have to relabel it to its
+ * base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1481,6 +1483,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid array_typelem;
Oid range_typeid = InvalidOid;
Oid range_typelem;
+ Oid multirange_typeid = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
@@ -1527,6 +1531,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1577,6 +1590,39 @@ check_generic_type_consistency(const Oid *actual_arg_types,
/* otherwise, they better match */
return false;
}
+ else
+ range_typelem = InvalidOid; /* keep compiler quiet */
+ }
+
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
}
if (have_anynonarray)
@@ -1680,13 +1726,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid array_typelem;
- Oid range_typelem;
+ Oid range_typelem = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
+ bool have_anyrange = (rettype == ANYRANGEOID);
+ bool have_anymultirange = (rettype == ANYMULTIRANGEOID);
/*
* Loop through the arguments to see if we have any that are polymorphic.
@@ -1744,7 +1794,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else if (decl_type == ANYRANGEOID)
{
- have_generics = true;
+ have_generics = have_anyrange = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
@@ -1762,6 +1812,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ have_generics = have_anymultirange = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
}
/*
@@ -1812,9 +1882,12 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- if (range_typeid == ANYRANGEOID && !have_anyelement)
+ if (range_typeid == ANYRANGEOID && !have_anyelement && !have_anymultirange)
{
- /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
+ /*
+ * Special case for ANYRANGE input: okay iff no ANYELEMENT or
+ * ANYMULTIRANGE
+ */
range_typelem = ANYELEMENTOID;
}
else
@@ -1846,6 +1919,71 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(range_typeid),
format_type_be(elem_typeid))));
}
+ else
+ range_typelem = InvalidOid;
+ }
+
+ /* Get the range type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ if (multirange_typeid == ANYMULTIRANGEOID && !have_anyrange && !have_anyelement)
+ {
+ /*
+ * Special case for ANYMULTIRANGE input: okay iff no ANYRANGE or
+ * ANYELEMENT
+ */
+ multirange_typelem = ANYRANGEOID;
+ }
+ else
+ {
+ multirange_typelem = get_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+ }
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ /*
+ * Infer the element type too if needed (if there is an ANYMULTIRANGE
+ * and ANYELEMENT, with no ANYRANGE).
+ */
+ if (!OidIsValid(elem_typeid))
+ {
+ elem_typeid = get_range_subtype(range_typeid);
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
}
if (!OidIsValid(elem_typeid))
@@ -1855,6 +1993,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -1927,6 +2066,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -1958,6 +2108,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
@@ -2006,8 +2172,7 @@ resolve_generic_type(Oid declared_type,
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
- context_declared_type == ANYENUMOID ||
- context_declared_type == ANYRANGEOID)
+ context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
@@ -2019,11 +2184,37 @@ resolve_generic_type(Oid declared_type,
format_type_be(context_actual_type))));
return array_typeid;
}
+ else if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid range_typelem = get_range_subtype(context_base_type);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+ Oid range_typelem = get_range_subtype(multirange_typelem);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
- declared_type == ANYENUMOID ||
- declared_type == ANYRANGEOID)
+ declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
@@ -2051,6 +2242,19 @@ resolve_generic_type(Oid declared_type,
"anyrange", format_type_be(context_base_type))));
return range_typelem;
}
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ /* Use the element type corresponding to actual type */
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
@@ -2059,6 +2263,74 @@ resolve_generic_type(Oid declared_type,
return context_actual_type;
}
}
+ else if (declared_type == ANYRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
+ else
+ {
+ /* We can't infer a range from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
+ else if (declared_type == ANYMULTIRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typeid = get_range_multirange(context_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return multirange_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else
+ {
+ /* We can't infer a multirange from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
else
{
/* declared_type isn't polymorphic, so return it as-is */
@@ -2173,6 +2445,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..7118dd0034 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..f9dd0378cc
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,2170 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ Oid typiofunc; /* range type's I/O function */
+ Oid typioparam; /* range type's I/O parameter */
+ FmgrInfo proc; /* lookup result for typiofunc */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData * get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks either either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str = NULL;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */ ;
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **) repalloc(ranges,
+ range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(
+ InputFunctionCall(&cache->proc, range_str_copy,
+ cache->typioparam, typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "Unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->proc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(
+ ReceiveFunctionCall(&cache->proc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range = RangeTypePGetDatum(ranges[i]);
+ uint32 range_len;
+ char *range_data;
+
+ range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
+ range_len = VARSIZE(range) - VARHDRSZ;
+ range_data = VARDATA(range);
+
+ pq_sendint32(buf, range_len);
+ pq_sendbytes(buf, range_data, range_len);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &cache->typiofunc);
+
+ if (!OidIsValid(cache->typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(cache->typiofunc, &cache->proc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ RangeType *range;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ bytelen += MAXALIGN(VARSIZE(range));
+ }
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ memcpy(ptr, range, VARSIZE(range));
+ ptr += MAXALIGN(VARSIZE(range));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of any ranges you like into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ * Returns the number of slots actually used,
+ * which may be less than input_range_count but never more.
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType * multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor2(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR, (errmsg("Can't construct multirange with a multi-dimensional array")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL element")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Construct multirange value from a single range.
+ * It'd be nice if we could just use multirange_constructor2
+ * for this case, but we need a non-variadic single-arg function
+ * to let us define a CAST from a range to its multirange.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ RangeType *range;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+
+ range = PG_GETARG_RANGE_P(0);
+
+ /* Make sure the range type matches. */
+ rngtypid = RangeTypeGetOid(range);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 1, &range));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ ereport(ERROR, (errmsg("Zero-param multirange constructor shouldn't have arguments")));
+}
+
+
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+multirange_union(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
+}
+
+/* multirange minus */
+Datum
+multirange_minus(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+multirange_intersect(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+ /*-----------------------------------------------
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1,
+ * but one extra won't hurt.
+ *-----------------------------------------------
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType * mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges1[0], ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1, MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
+ return false;
+
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
+ return false;
+ }
+
+ /* All ranges in mr2 are satisfied */
+ return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 0d9e55cdcf..51b9ac907c 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -51,6 +51,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
+Datum
+binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
+binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_array_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
Datum
binary_upgrade_set_next_toast_pg_type_oid(PG_FUNCTION_ARGS)
{
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index ad0e363c70..152e77f44c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -184,6 +185,30 @@ anyrange_out(PG_FUNCTION_ARGS)
return range_out(fcinfo);
}
+/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
/*
* void_in - input routine for pseudo-type VOID.
*
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index b95132b714..37b3f40499 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -431,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -452,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -475,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -485,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -495,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -505,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -515,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -957,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -969,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -993,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1101,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1110,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1132,9 +1180,81 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
}
+/* range, range -> range, range functions */
+
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
+ {
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
+ }
+
+ return false;
+}
+
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
+}
+
+
/* Btree support */
/* btree comparator */
@@ -1144,12 +1264,6 @@ range_cmp(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
- RangeBound lower1,
- lower2;
- RangeBound upper1,
- upper2;
- bool empty1,
- empty2;
int cmp;
check_stack_depth(); /* recurses when subtype is a range type */
@@ -1160,6 +1274,28 @@ range_cmp(PG_FUNCTION_ARGS)
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+ cmp = range_cmp_internal(typcache, r1, r2);
+
+ PG_FREE_IF_COPY(r1, 0);
+ PG_FREE_IF_COPY(r2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
@@ -1177,10 +1313,7 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
- PG_FREE_IF_COPY(r1, 0);
- PG_FREE_IF_COPY(r2, 1);
-
- PG_RETURN_INT32(cmp);
+ return cmp;
}
/* inequality operators using the range_cmp function */
@@ -1218,13 +1351,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1363,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1404,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1433,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1471,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1769,6 +1916,21 @@ range_get_flags(const RangeType *range)
return *((char *) range + VARSIZE(range) - 1);
}
+/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
@@ -1937,6 +2099,45 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
b1->val, b2->val));
}
+/*
+ * Compares two ranges so we can qsort them.
+ * This expects that you give qsort a RangeType **,
+ * so the RangeTypes can be in diverse locations,
+ * as long as you have a list of pointers to them all.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
/*
* Build an empty range value of the type indicated by the typcache entry.
*/
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index fb0599f7f1..56a145c666 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2468,6 +2468,16 @@ type_is_range(Oid typid)
return (get_typtype(typid) == TYPTYPE_RANGE);
}
+/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
/*
* get_type_category_preferred
*
@@ -3176,6 +3186,58 @@ get_range_collation(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngmultitypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/* ---------- PG_MULTIRANGE CACHE ---------- */
+
+/*
+ * get_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_multirange_subtype(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 53d9ddf159..fb9179c42a 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -508,6 +508,17 @@ static const struct cachedesc cacheinfo[] = {
},
4
},
+ {RangeRelationId, /* MULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngmultitypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{NamespaceRelationId, /* NAMESPACENAME */
NamespaceNameIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index cdf6331a97..6677bc8bdc 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -278,6 +278,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -294,6 +295,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheConstrCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -500,8 +504,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -538,7 +542,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -563,7 +567,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -588,7 +592,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -640,6 +644,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -684,6 +695,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -758,6 +776,16 @@ lookup_type_cache(Oid type_id, int flags)
load_rangetype_info(typentry);
}
+ /*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
/*
* If requested, get information about a domain type
*/
@@ -870,6 +898,33 @@ load_rangetype_info(TypeCacheEntry *typentry)
}
+/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Form_pg_range pg_range;
+ HeapTuple tup;
+ Oid rangetypeOid;
+
+ /* get information from pg_range */
+ tup = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(typentry->type_id));
+ /* should not fail, since we already checked typtype ... */
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+ pg_range = (Form_pg_range) GETSTRUCT(tup);
+
+ rangetypeOid = pg_range->rngtypid;
+
+ ReleaseSysCache(tup);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
+
+
/*
* load_domaintype_info --- helper routine to set up domain constraint info
*
@@ -1469,11 +1524,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1516,6 +1571,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index b7eee3da1d..defc801f7a 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -470,11 +470,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anynonarray = false;
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
Oid anycollation = InvalidOid;
int i;
@@ -500,12 +502,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
case ANYRANGEOID:
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_anymultirange_result = true;
+ break;
default:
break;
}
}
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/*
@@ -533,6 +538,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (!OidIsValid(anyrange_type))
anyrange_type = get_call_expr_argtype(call_expr, i);
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(anymultirange_type))
+ anymultirange_type = get_call_expr_argtype(call_expr, i);
+ break;
default:
break;
}
@@ -540,7 +549,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/* If nothing found, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -561,19 +570,131 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ anymultirange_type = mltrngtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
/* Enforce ANYNONARRAY if needed */
if (have_anynonarray && type_is_array(anyelement_type))
@@ -640,6 +761,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
default:
break;
}
@@ -664,9 +793,11 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
int inargno;
int i;
@@ -725,6 +856,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ have_anymultirange_result = true;
+ else
+ {
+ if (!OidIsValid(anymultirange_type))
+ {
+ anymultirange_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anymultirange_type))
+ return false;
+ }
+ argtypes[i] = anymultirange_type;
+ }
+ break;
default:
break;
}
@@ -734,12 +880,12 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
/* Done? */
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/* If no input polymorphics, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -760,19 +906,132 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ rngtype = get_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ }
+ else
+ {
+ return false;
+ }
+ }
/* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */
@@ -792,6 +1051,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = anymultirange_type;
+ break;
default:
break;
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index ef1539044f..1c01434c11 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -274,7 +274,8 @@ static void dumpSearchPath(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type);
+ bool force_array_type,
+ bool include_multirange_type);
static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
static void binary_upgrade_set_pg_class_oids(Archive *fout,
@@ -1584,7 +1585,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4291,15 +4292,49 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
+static Oid
+get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+{
+ /*
+ * If the old version didn't assign an array type, but the new version
+ * does, we must select an unused type OID to assign. This currently only
+ * happens for domains, when upgrading pre-v11 to v11 and up.
+ *
+ * Note: local state here is kind of ugly, but we must have some, since we
+ * mustn't choose the same unused OID more than once.
+ */
+ static Oid next_possible_free_oid = FirstNormalObjectId;
+ PGresult *res;
+ bool is_dup;
+
+ do
+ {
+ ++next_possible_free_oid;
+ printfPQExpBuffer(upgrade_query,
+ "SELECT EXISTS(SELECT 1 "
+ "FROM pg_catalog.pg_type "
+ "WHERE oid = '%u'::pg_catalog.oid);",
+ next_possible_free_oid);
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+ is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
+ PQclear(res);
+ } while (is_dup);
+
+ return next_possible_free_oid;
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type)
+ bool force_array_type,
+ bool include_multirange_type)
{
PQExpBuffer upgrade_query = createPQExpBuffer();
PGresult *res;
Oid pg_type_array_oid;
+ Oid pg_type_multirange_oid;
+ Oid pg_type_multirange_array_oid;
appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
@@ -4320,33 +4355,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQclear(res);
if (!OidIsValid(pg_type_array_oid) && force_array_type)
- {
- /*
- * If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
- *
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
- */
- static Oid next_possible_free_oid = FirstNormalObjectId;
- bool is_dup;
-
- do
- {
- ++next_possible_free_oid;
- printfPQExpBuffer(upgrade_query,
- "SELECT EXISTS(SELECT 1 "
- "FROM pg_catalog.pg_type "
- "WHERE oid = '%u'::pg_catalog.oid);",
- next_possible_free_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
- is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
- PQclear(res);
- } while (is_dup);
-
- pg_type_array_oid = next_possible_free_oid;
- }
+ pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
if (OidIsValid(pg_type_array_oid))
{
@@ -4357,6 +4366,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
pg_type_array_oid);
}
+ /*
+ * Pre-set the multirange type oid and its own array type oid.
+ */
+ if (include_multirange_type)
+ {
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBuffer(upgrade_query,
+ "SELECT t.oid, t.typarray "
+ "FROM pg_catalog.pg_type t "
+ "JOIN pg_catalog.pg_range r "
+ "ON t.oid = r.rngmultitypid "
+ "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
+ pg_type_oid);
+
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+
+ pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+ pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
+
+ PQclear(res);
+ }
+ else
+ {
+ pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ }
+
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_oid);
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_array_oid);
+ }
+
destroyPQExpBuffer(upgrade_query);
}
@@ -4390,7 +4439,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
- pg_type_oid, false);
+ pg_type_oid, false, false);
if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
{
@@ -4939,6 +4988,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
@@ -8053,9 +8107,12 @@ getProcLangs(Archive *fout, int *numProcLangs)
/*
* getCasts
- * get basic information about every cast in the system
+ * get basic information about most casts in the system
*
* numCasts is set to the number of casts read in
+ *
+ * Skip casts from a range to its multirange, since we'll create those
+ * automatically.
*/
CastInfo *
getCasts(Archive *fout, int *numCasts)
@@ -8073,7 +8130,20 @@ getCasts(Archive *fout, int *numCasts)
int i_castcontext;
int i_castmethod;
- if (fout->remoteVersion >= 80400)
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBufferStr(query, "SELECT tableoid, oid, "
+ "castsource, casttarget, castfunc, castcontext, "
+ "castmethod "
+ "FROM pg_cast c "
+ "WHERE NOT EXISTS ( "
+ "SELECT 1 FROM pg_range r "
+ "WHERE c.castsource = r.rngtypid "
+ "AND c.casttarget = r.rngmultitypid "
+ ") "
+ "ORDER BY 3,4");
+ }
+ else if (fout->remoteVersion >= 80400)
{
appendPQExpBufferStr(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
@@ -10261,7 +10331,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
qualtypname);
@@ -10387,7 +10457,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, true);
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
qualtypname);
@@ -10493,7 +10563,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
qualtypname);
@@ -10698,7 +10768,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
@@ -10885,7 +10955,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- true); /* force array type */
+ true, /* force array type */
+ false); /* force multirange type */
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -11073,7 +11144,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
{
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
}
@@ -11347,7 +11418,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
stinfo->baseType->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
fmtQualifiedDumpable(stinfo));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 21004e5078..63e0ba7116 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -175,6 +175,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 0b205a0dc6..3c113fa3bd 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -137,8 +137,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 12d94fe1b3..01d95117cd 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,6 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 8be303870f..566f9364c5 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -327,6 +327,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_rngmultitypid_index, 8001, on pg_range using btree(rngmultitypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index ffabe275c0..03bab74253 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..827ac4f03c 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 75c0152b66..cf0cac5641 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -285,6 +285,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
@@ -445,6 +447,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
@@ -1263,12 +1270,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index 6ef8b8a4e7..14277c8fdf 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -512,4 +512,17 @@
{ castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
castcontext => 'e', castmethod => 'f' },
+# range to multirange
+{ castsource => 'int4range', casttarget => 'int4multirange', castfunc => 'int4multirange(int4range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'int8range', casttarget => 'int8multirange', castfunc => 'int8multirange(int8range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'numrange', casttarget => 'nummultirange', castfunc => 'nummultirange(numrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'daterange', casttarget => 'datemultirange', castfunc => 'datemultirange(daterange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tsrange', casttarget => 'tsmultirange', castfunc => 'tsmultirange(tsrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tstzrange', casttarget => 'tstzmultirange', castfunc => 'tstzmultirange(tstzrange)',
+ castcontext => 'e', castmethod => 'f' },
]
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index 1b81b52df6..f23ba28d9c 100644
--- a/src/include/catalog/pg_cast.h
+++ b/src/include/catalog/pg_cast.h
@@ -23,6 +23,8 @@
#include "catalog/genbki.h"
#include "catalog/pg_cast_d.h"
+#include "catalog/dependency.h"
+
/* ----------------
* pg_cast definition. cpp turns this into
* typedef struct FormData_pg_cast
@@ -87,4 +89,11 @@ typedef enum CoercionMethod
#endif /* EXPOSE_TO_CLIENT_CODE */
+extern ObjectAddress CastCreate(Oid sourcetypeid,
+ Oid targettypeid,
+ Oid funcid,
+ char castcontext,
+ char castmethod,
+ DependencyType behavior);
+
#endif /* PG_CAST_H */
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index ab2f50c9eb..eebebbcdb4 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 7c135da3b1..24f4a32203 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3297,5 +3297,174 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'contsel', oprjoin => 'contjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 26227df216..901fd7b303 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 07a86c7b7b..d89b3313bf 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9649,6 +9649,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9698,6 +9702,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9766,6 +9777,258 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8114',
+ proname => 'multirange_union', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union' },
+{ oid => '8120',
+ proname => 'multirange_minus', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus' },
+{ oid => '8126',
+ proname => 'multirange_intersect', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8146', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 't',
+ prorettype => 'int4multirange', proargtypes => 'int4range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8147', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 't',
+ prorettype => 'nummultirange', proargtypes => 'numrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8148', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 't',
+ prorettype => 'tsmultirange', proargtypes => 'tsrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8149', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 't',
+ prorettype => 'tstzmultirange', proargtypes => 'tstzrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8150', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 't',
+ prorettype => 'datemultirange', proargtypes => 'daterange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8151', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 't',
+ prorettype => 'int8multirange', proargtypes => 'int8range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8152', descr => 'anymultirange cast',
+ proname => 'multirange', proisstrict => 't',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
@@ -10148,6 +10411,14 @@
proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
+{ oid => '8144', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
+{ oid => '8145', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
{ oid => '3585', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index 479754c245..10060255c9 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ rngmultitypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', rngmultitypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', rngmultitypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', rngmultitypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ rngmultitypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ rngmultitypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index 98172bb1b6..3efd463c6d 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -45,6 +45,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* subtype difference as a float8, or 0 */
regproc rngsubdiff BKI_LOOKUP(pg_proc);
+
+ /* OID of the range's multirange type */
+ Oid rngmultitypid BKI_LOOKUP(pg_type);
} FormData_pg_range;
/* ----------------
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 4cf2b9df7b..beaa70dce6 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -486,6 +486,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -594,5 +628,10 @@
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index e1a5ab3df3..600ac58c9d 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -258,6 +258,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -269,6 +270,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPCATEGORY_ENUM 'E'
#define TYPCATEGORY_GEOMETRIC 'G'
#define TYPCATEGORY_NETWORK 'I' /* think INET */
+#define TYPCATEGORY_MULTIRANGE 'M'
#define TYPCATEGORY_NUMERIC 'N'
#define TYPCATEGORY_PSEUDOTYPE 'P'
#define TYPCATEGORY_RANGE 'R'
@@ -284,7 +286,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#endif /* EXPOSE_TO_CLIENT_CODE */
@@ -343,4 +346,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index fc18d64347..7d0a4a9ea5 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
+extern Oid AssignTypeMultirangeOid(void);
+extern Oid AssignTypeMultirangeArrayOid(void);
extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 370f62a133..7d1674a3b6 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -154,6 +154,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -180,6 +181,8 @@ extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
extern Oid get_range_collation(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..8779bd1f6e
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ char vl_len_[4]; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern MultirangeType * multirange_minus_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType * multirange_intersect_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType * range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType * make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType * make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 0cbbf09033..204aee4054 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
@@ -92,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -115,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -125,6 +141,7 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
@@ -132,8 +149,12 @@ extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f27b73d76d..bc51bbeb54 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -66,6 +66,7 @@ enum SysCacheIdentifier
INDEXRELID,
LANGNAME,
LANGOID,
+ MULTIRANGETYPE,
NAMESPACENAME,
NAMESPACEOID,
OPERNAMENSP,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index 66ff17dbd5..fe240ea004 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -98,6 +98,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_canonical_finfo; /* canonicalization function, if any */
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
+ /*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype;
+
/*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
@@ -141,6 +146,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 4f6b36b145..d759bf3aee 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -511,6 +511,8 @@ do_compile(FunctionCallInfo fcinfo,
rettypeid = INT4ARRAYOID;
else if (rettypeid == ANYRANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2087,6 +2089,7 @@ build_datatype(HeapTuple typeTup, int32 typmod,
case TYPTYPE_BASE:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
+ case TYPTYPE_MULTIRANGE:
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_COMPOSITE:
@@ -2501,6 +2504,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYRANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..778699a961 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -141,6 +141,7 @@ owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
owner of type deptest_range
+owner of type deptest_multirange
owner of table deptest2
owner of sequence ss1
owner of type deptest_t
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..723f2408b0
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,2395 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+ int4multirange
+----------------
+ {}
+(1 row)
+
+select int4range(1, 3)::int4multirange;
+ int4range
+-----------
+ {[1,3)}
+(1 row)
+
+select int4range(1, null)::int4multirange;
+ int4range
+-----------
+ {[1,)}
+(1 row)
+
+select int4range(null, null)::int4multirange;
+ int4range
+-----------
+ {(,)}
+(1 row)
+
+select 'empty'::textrange::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textrange('a', 'c')::textmultirange;
+ textrange
+-----------
+ {[a,c)}
+(1 row)
+
+select textrange('a', null)::textmultirange;
+ textrange
+-----------
+ {[a,)}
+(1 row)
+
+select textrange(null, null)::textmultirange;
+ textrange
+-----------
+ {(,)}
+(1 row)
+
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT nummultirange() + nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT nummultirange() - nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() * nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+ mr_polymorphic
+----------------
+ {[1,4)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index fb6c029e3d..f4b45a86b5 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -206,6 +213,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -224,6 +233,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -329,13 +340,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
@@ -343,16 +355,19 @@ ORDER BY 2;
2502 | anyarray_recv
2312 | anyelement_in
3504 | anyenum_in
+ 8002 | anymultirange_in
2777 | anynonarray_in
3832 | anyrange_in
750 | array_in
2400 | array_recv
3506 | enum_in
3532 | enum_recv
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(13 rows)
+(16 rows)
-- Look for functions that accept cstring and are neither datatype input
-- functions nor encoding conversion functions. It's almost never a good
@@ -2139,13 +2154,14 @@ ORDER BY 1, 2, 3;
| float_ops | float4_ops | real
| float_ops | float8_ops | double precision
| jsonb_ops | jsonb_ops | jsonb
+ | multirange_ops | multirange_ops | anymultirange
| numeric_ops | numeric_ops | numeric
| range_ops | range_ops | anyrange
| record_image_ops | record_image_ops | record
| record_ops | record_ops | record
| tsquery_ops | tsquery_ops | tsquery
| tsvector_ops | tsvector_ops | tsvector
-(14 rows)
+(15 rows)
-- **************** pg_index ****************
-- Look for illegal values in pg_index fields.
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 220f2d96cb..6d00a3efab 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1371,7 +1389,7 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
@@ -1482,6 +1500,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1490,6 +1509,16 @@ select * from outparam_succeed(int4range(1,2));
[1,2) | foo
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1498,6 +1527,7 @@ select * from inoutparam_succeed(int4range(1,2));
2 | [1,2)
(1 row)
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
select * from table_succeed(123, int4range(1,11));
@@ -1510,14 +1540,14 @@ select * from table_succeed(123, int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 192445878d..d9ce961be2 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
numrange_test|t
onek|t
onek2|t
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index cd7fc03b04..60a9b4d22f 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -193,18 +193,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -238,17 +239,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -316,18 +318,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -361,17 +364,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -629,3 +633,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
+ rngtypid | rngsubtype | rngmultitypid
+----------+------------+---------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index d2b17dd3ea..5355da7b01 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index acba391332..1f677f63af 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..0f70919e1a
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,630 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+select int4range(1, 3)::int4multirange;
+select int4range(1, null)::int4multirange;
+select int4range(null, null)::int4multirange;
+select 'empty'::textrange::textmultirange;
+select textrange('a', 'c')::textmultirange;
+select textrange('a', null)::textmultirange;
+select textrange(null, null)::textmultirange;
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT nummultirange() + nummultirange();
+SELECT nummultirange() + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT nummultirange() - nummultirange();
+SELECT nummultirange() - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+SELECT nummultirange() * nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 8351b6469a..1ac7a55925 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -273,13 +284,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- Look for functions that accept cstring and are neither datatype input
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 72d80bc9d4..07c9bcf6d3 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -512,16 +516,25 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fed413bf98..2e34bc8b65 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -151,7 +151,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -180,7 +180,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -232,7 +232,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -261,7 +261,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -465,3 +465,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
--
2.20.1
[text/x-diff] v11-0002-silence-compiler-warning.patch (825B, ../../20200304022849.GA5402@alvherre.pgsql/3-v11-0002-silence-compiler-warning.patch)
download | inline diff:
From b7938e64f45e6b87695357570100118d9cf64c3f Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 28 Nov 2019 19:08:46 -0300
Subject: [PATCH v11 2/4] silence compiler warning
---
src/backend/parser/parse_coerce.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 47d17f43a4..ff1310c8f4 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1593,6 +1593,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
else
range_typelem = InvalidOid; /* keep compiler quiet */
}
+ else
+ range_typelem = InvalidOid; /* keep compiler quiet */
/* Get the element type based on the multirange type, if we have one */
if (OidIsValid(multirange_typeid))
--
2.20.1
[text/x-diff] v11-0003-whitespace-tweaks.patch (3.2K, ../../20200304022849.GA5402@alvherre.pgsql/4-v11-0003-whitespace-tweaks.patch)
download | inline diff:
From 5e25fb6f93f0b646b85c502aeb7adfa7f5cd334c Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Tue, 3 Mar 2020 15:09:56 -0300
Subject: [PATCH v11 3/4] whitespace tweaks
---
src/backend/utils/adt/multirangetypes.c | 15 +++++++--------
src/include/catalog/pg_range.h | 6 +++---
2 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index f9dd0378cc..0c9afd5448 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -174,12 +174,11 @@ multirange_in(PG_FUNCTION_ARGS)
if (range_capacity == range_count)
{
range_capacity *= 2;
- ranges = (RangeType **) repalloc(ranges,
- range_capacity * sizeof(RangeType *));
+ ranges = (RangeType **)
+ repalloc(ranges, range_capacity * sizeof(RangeType *));
}
ranges_seen++;
- range = DatumGetRangeTypeP(
- InputFunctionCall(&cache->proc, range_str_copy,
+ range = DatumGetRangeTypeP(InputFunctionCall(&cache->proc, range_str_copy,
cache->typioparam, typmod));
if (!RangeIsEmpty(range))
ranges[range_count++] = range;
@@ -376,11 +375,11 @@ multirange_typanalyze(PG_FUNCTION_ARGS)
* pointer to a type cache entry.
*/
static MultirangeIOData *
-get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid, IOFuncSelector func)
{
MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
- if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ if (cache == NULL || cache->typcache->type_id != rngtypid)
{
int16 typlen;
bool typbyval;
@@ -389,9 +388,9 @@ get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector
cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(MultirangeIOData));
- cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ cache->typcache = lookup_type_cache(rngtypid, TYPECACHE_MULTIRANGE_INFO);
if (cache->typcache->rngtype == NULL)
- elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ elog(ERROR, "type %u is not a multirange type", rngtypid);
/* get_type_io_data does more than we need, but is convenient */
get_type_io_data(cache->typcache->rngtype->type_id,
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index 3efd463c6d..60a8fde518 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -34,6 +34,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* OID of range's element type (subtype) */
Oid rngsubtype BKI_LOOKUP(pg_type);
+ /* OID of the range's multirange type */
+ Oid rngmultitypid BKI_LOOKUP(pg_type);
+
/* collation for this range type, or 0 */
Oid rngcollation BKI_DEFAULT(0);
@@ -45,9 +48,6 @@ CATALOG(pg_range,3541,RangeRelationId)
/* subtype difference as a float8, or 0 */
regproc rngsubdiff BKI_LOOKUP(pg_proc);
-
- /* OID of the range's multirange type */
- Oid rngmultitypid BKI_LOOKUP(pg_type);
} FormData_pg_range;
/* ----------------
--
2.20.1
[text/x-diff] v11-0004-Various-tweaks-mostly-naming.patch (13.6K, ../../20200304022849.GA5402@alvherre.pgsql/5-v11-0004-Various-tweaks-mostly-naming.patch)
download | inline diff:
From 1d4292c051526d6d4704ce2a95597a1da82bebbb Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Tue, 11 Feb 2020 20:12:20 -0300
Subject: [PATCH v11 4/4] Various tweaks, mostly naming
Got rid of TYPCATEGORY_MULTIRANGE. It doesn't serve any purpose that I
can see, so I just switched that to TYPCATEGORY_RANGE.
load_multirangetype_info() pointlessly open-coded
get_range_multirange_subtype(). Simplify.
Name changes:
get_multirange_subtype -> get_range_multirange_subtype
This is more in line with how we name lsyscache.c functions, given
that the cache it serves is on pg_range. (Apparently in earlier
versions of the patch there was a pg_multirange catalog, but not
anymore)
MULTIRANGETYPE -> RANGEMULTIRANGE (the syscache)
Mostly the same reasons as above. The naming also makes us move it
next to RANGETYPE; they're both in pg_range. (It was in the place
where pg_multirange would be, alphabetically.)
---
src/backend/commands/typecmds.c | 2 +-
src/backend/parser/parse_coerce.c | 10 +++++-----
src/backend/utils/cache/lsyscache.c | 10 ++++------
src/backend/utils/cache/syscache.c | 23 ++++++++++++-----------
src/backend/utils/cache/typcache.c | 15 ++-------------
src/backend/utils/fmgr/funcapi.c | 4 ++--
src/include/catalog/pg_type.dat | 12 ++++++------
src/include/catalog/pg_type.h | 1 -
src/include/utils/lsyscache.h | 2 +-
src/include/utils/syscache.h | 2 +-
src/include/utils/typcache.h | 2 +-
11 files changed, 35 insertions(+), 48 deletions(-)
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index a8dea8d58e..bd792234f8 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1589,7 +1589,7 @@ DefineRange(CreateRangeStmt *stmt)
GetUserId(), /* owner's ID */
-1, /* internal size (always varlena) */
TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
- TYPCATEGORY_MULTIRANGE, /* type-category (multirange type) */
+ TYPCATEGORY_RANGE, /* type-category (range type) */
false, /* multirange types are never preferred */
DEFAULT_TYPDELIM, /* array element delimiter */
F_MULTIRANGE_IN, /* input procedure */
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index ff1310c8f4..27bfc4bf8c 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1599,7 +1599,7 @@ check_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the multirange type, if we have one */
if (OidIsValid(multirange_typeid))
{
- multirange_typelem = get_multirange_subtype(multirange_typeid);
+ multirange_typelem = get_range_multirange_subtype(multirange_typeid);
if (!OidIsValid(multirange_typelem))
return false; /* should be a multirange, but isn't */
@@ -1938,7 +1938,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else
{
- multirange_typelem = get_multirange_subtype(multirange_typeid);
+ multirange_typelem = get_range_multirange_subtype(multirange_typeid);
if (!OidIsValid(multirange_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -2202,7 +2202,7 @@ resolve_generic_type(Oid declared_type,
else if (context_declared_type == ANYMULTIRANGEOID)
{
Oid context_base_type = getBaseType(context_actual_type);
- Oid multirange_typelem = get_multirange_subtype(context_base_type);
+ Oid multirange_typelem = get_range_multirange_subtype(context_base_type);
Oid range_typelem = get_range_subtype(multirange_typelem);
Oid array_typeid = get_array_type(range_typelem);
@@ -2248,7 +2248,7 @@ resolve_generic_type(Oid declared_type,
{
/* Use the element type corresponding to actual type */
Oid context_base_type = getBaseType(context_actual_type);
- Oid multirange_typelem = get_multirange_subtype(context_base_type);
+ Oid multirange_typelem = get_range_multirange_subtype(context_base_type);
if (!OidIsValid(multirange_typelem))
ereport(ERROR,
@@ -2281,7 +2281,7 @@ resolve_generic_type(Oid declared_type,
else if (context_declared_type == ANYMULTIRANGEOID)
{
Oid context_base_type = getBaseType(context_actual_type);
- Oid multirange_typelem = get_multirange_subtype(context_base_type);
+ Oid multirange_typelem = get_range_multirange_subtype(context_base_type);
if (!OidIsValid(multirange_typelem))
ereport(ERROR,
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 56a145c666..7ba93c43d7 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3133,7 +3133,7 @@ get_namespace_name_or_temp(Oid nspid)
return get_namespace_name(nspid);
}
-/* ---------- PG_RANGE CACHE ---------- */
+/* ---------- PG_RANGE CACHES ---------- */
/*
* get_range_subtype
@@ -3211,20 +3211,18 @@ get_range_multirange(Oid rangeOid)
return InvalidOid;
}
-/* ---------- PG_MULTIRANGE CACHE ---------- */
-
/*
- * get_multirange_subtype
+ * get_range_multirange_subtype
* Returns the subtype of a given multirange type
*
* Returns InvalidOid if the type is not a multirange type.
*/
Oid
-get_multirange_subtype(Oid multirangeOid)
+get_range_multirange_subtype(Oid multirangeOid)
{
HeapTuple tp;
- tp = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(multirangeOid));
+ tp = SearchSysCache1(RANGEMULTIRANGE, ObjectIdGetDatum(multirangeOid));
if (HeapTupleIsValid(tp))
{
Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index fb9179c42a..7654331a59 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -508,17 +508,6 @@ static const struct cachedesc cacheinfo[] = {
},
4
},
- {RangeRelationId, /* MULTIRANGE */
- RangeMultirangeTypidIndexId,
- 1,
- {
- Anum_pg_range_rngmultitypid,
- 0,
- 0,
- 0
- },
- 4
- },
{NamespaceRelationId, /* NAMESPACENAME */
NamespaceNameIndexId,
1,
@@ -662,6 +651,18 @@ static const struct cachedesc cacheinfo[] = {
},
64
},
+ {RangeRelationId, /* RANGEMULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngmultitypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+
{RangeRelationId, /* RANGETYPE */
RangeTypidIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 6677bc8bdc..9383b24f65 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -897,7 +897,6 @@ load_rangetype_info(TypeCacheEntry *typentry)
typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
}
-
/*
* load_multirangetype_info --- helper routine to set up multirange type
* information
@@ -905,26 +904,16 @@ load_rangetype_info(TypeCacheEntry *typentry)
static void
load_multirangetype_info(TypeCacheEntry *typentry)
{
- Form_pg_range pg_range;
- HeapTuple tup;
Oid rangetypeOid;
- /* get information from pg_range */
- tup = SearchSysCache1(MULTIRANGETYPE, ObjectIdGetDatum(typentry->type_id));
- /* should not fail, since we already checked typtype ... */
- if (!HeapTupleIsValid(tup))
+ rangetypeOid = get_range_multirange_subtype(typentry->type_id);
+ if (!OidIsValid(rangetypeOid))
elog(ERROR, "cache lookup failed for multirange type %u",
typentry->type_id);
- pg_range = (Form_pg_range) GETSTRUCT(tup);
-
- rangetypeOid = pg_range->rngtypid;
-
- ReleaseSysCache(tup);
typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
}
-
/*
* load_domaintype_info --- helper routine to set up domain constraint info
*
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index defc801f7a..afc617939f 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -683,7 +683,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
ANYRANGEOID);
/* check for inconsistent range and multirange results */
- rngtype = get_multirange_subtype(mltrngtype);
+ rngtype = get_range_multirange_subtype(mltrngtype);
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
@@ -1020,7 +1020,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
ANYRANGEOID);
/* check for inconsistent range and multirange results */
- rngtype = get_multirange_subtype(mltrngtype);
+ rngtype = get_range_multirange_subtype(mltrngtype);
if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
return false;
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index beaa70dce6..7d4bf4c5d8 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -489,34 +489,34 @@
# multirange types
{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
- typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
typreceive => 'multirange_recv', typsend => 'multirange_send',
typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
- typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
typreceive => 'multirange_recv', typsend => 'multirange_send',
typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
{ oid => '8013', array_type_oid => '8014',
descr => 'multirange of timestamps without time zone',
typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
- typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
typreceive => 'multirange_recv', typsend => 'multirange_send',
typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
{ oid => '8015', array_type_oid => '8016',
descr => 'multirange of timestamps with time zone',
typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
- typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
typreceive => 'multirange_recv', typsend => 'multirange_send',
typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
- typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
typreceive => 'multirange_recv', typsend => 'multirange_send',
typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
- typcategory => 'M', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
typreceive => 'multirange_recv', typsend => 'multirange_send',
typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 600ac58c9d..c113320b9c 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -270,7 +270,6 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPCATEGORY_ENUM 'E'
#define TYPCATEGORY_GEOMETRIC 'G'
#define TYPCATEGORY_NETWORK 'I' /* think INET */
-#define TYPCATEGORY_MULTIRANGE 'M'
#define TYPCATEGORY_NUMERIC 'N'
#define TYPCATEGORY_PSEUDOTYPE 'P'
#define TYPCATEGORY_RANGE 'R'
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 7d1674a3b6..fffd4c5a45 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -182,7 +182,7 @@ extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
extern Oid get_range_collation(Oid rangeOid);
extern Oid get_range_multirange(Oid rangeOid);
-extern Oid get_multirange_subtype(Oid multirangeOid);
+extern Oid get_range_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index bc51bbeb54..e8f393520a 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -66,7 +66,6 @@ enum SysCacheIdentifier
INDEXRELID,
LANGNAME,
LANGOID,
- MULTIRANGETYPE,
NAMESPACENAME,
NAMESPACEOID,
OPERNAMENSP,
@@ -80,6 +79,7 @@ enum SysCacheIdentifier
PUBLICATIONOID,
PUBLICATIONREL,
PUBLICATIONRELMAP,
+ RANGEMULTIRANGE,
RANGETYPE,
RELNAMENSP,
RELOID,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index fe240ea004..9957be4640 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -101,7 +101,7 @@ typedef struct TypeCacheEntry
/*
* Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
*/
- struct TypeCacheEntry *rngtype;
+ struct TypeCacheEntry *rngtype; /* underlying range type */
/*
* Domain's base type and typmod if it's a domain type. Zeroes if not
--
2.20.1
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-03-04 21:33 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-04 22:26 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2 siblings, 1 reply; 152+ messages in thread
From: Alvaro Herrera @ 2020-03-04 21:33 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
I came across an interesting thing, namely multirange_canonicalize()'s
use of qsort_arg with a callback of range_compare(). range_compare()
calls range_deserialize() (non-trivial parsing) for each input range;
multirange_canonicalize() later does a few extra deserialize calls of
its own. Call me a premature optimization guy if you will, but I think
it makes sense to have a different struct (let's call it
"InMemoryRange") which stores the parsed representation of each range;
then we can deserialize all ranges up front, and use that as many times
as needed, without having to deserialize each range every time.
While I'm at this, why not name the new file simply multiranges.c
instead of multirangetypes.c?
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-04 21:33 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-03-04 22:26 ` Paul Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 0 replies; 152+ messages in thread
From: Paul Jungwirth @ 2020-03-04 22:26 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
Thanks for looking at this again!
On 3/4/20 1:33 PM, Alvaro Herrera wrote:
> I came across an interesting thing, namely multirange_canonicalize()'s
> use of qsort_arg with a callback of range_compare(). range_compare()
> calls range_deserialize() (non-trivial parsing) for each input range;
> multirange_canonicalize() later does a few extra deserialize calls of
> its own. Call me a premature optimization guy if you will, but I think
> it makes sense to have a different struct (let's call it
> "InMemoryRange") which stores the parsed representation of each range;
> then we can deserialize all ranges up front, and use that as many times
> as needed, without having to deserialize each range every time.
I don't know, this sounds like a drastic change. I agree that
multirange_deserialize and range_deserialize do a lot of copying (not
really any parsing though, and they both assume their inputs are already
de-TOASTED). But they are used very extensively, so if you wanted to
remove them you'd have to rewrite a lot.
I interpreted the intention of range_deserialize to be a way to keep the
range struct fairly "private" and give a standard interface to
extracting its attributes. Its motive seems akin to deconstruct_array.
So I wrote multirange_deserialize to follow that principle. Both
functions also handle memory alignment issues for you. With
multirange_deserialize, there isn't actually much structure (just the
list of ranges), so perhaps you could more easily omit it and give
callers direct access into the multirange contents. That still seems
risky though, and less well encapsulated.
My preference would be to see if these functions are really a
performance problem first, and only redo the in-memory structures if
they are. Also that seems like something you could do as a separate
project. (I wouldn't mind working on it myself, although I'd prefer to
do actual temporal database features first.) There are no
backwards-compatibility concerns to changing the in-memory structure,
right? (Even if there are, it's too late to avoid them for ranges.)
> While I'm at this, why not name the new file simply multiranges.c
> instead of multirangetypes.c?
As someone who doesn't do a lot of Postgres hacking, I tried to follow
the approach in rangetypes.c as closely as I could, especially for
naming things. So I named the file multirangetypes.c because there was
already rangetypes.c. But also I can see how the "types" emphasizes that
ranges and multiranges are not concrete types themselves, but more like
abstract data types or generics (like arrays).
Yours,
--
Paul ~{:-)
pj@illuminatedcomputing.com
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-03-07 20:19 ` Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2 siblings, 2 replies; 152+ messages in thread
From: Tom Lane @ 2020-03-07 20:19 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Paul A Jungwirth <pj@illuminatedcomputing.com>; Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> [ v11 patches ]
The cfbot isn't too happy with this; it's getting differently-ordered
results than you apparently did for the list of owned objects in
dependency.out's DROP OWNED BY test. Not sure why that should be ---
it seems like af6550d34 should have ensured that there's only one
possible ordering.
However, what I'm on about right at the moment is that I don't think
there should be any delta in that test at all. As far as I can see,
the design idea here is that multiranges will be automatically created
over range types, and the user doesn't need to do that. To my mind,
that means that they're an implementation detail and should not show up as
separately-owned objects, any more than an autogenerated array type does.
So somewhere there's a missing bit of code, or more than one missing bit,
to make multiranges act as derived types, the way arrays are.
regards, tom lane
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
@ 2020-03-07 21:06 ` Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:20 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 22:13 ` Re: range_agg David Fetter <david@fetter.org>
2020-03-09 17:53 ` Re: range_agg Robert Haas <robertmhaas@gmail.com>
2020-03-10 01:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
1 sibling, 4 replies; 152+ messages in thread
From: Tom Lane @ 2020-03-07 21:06 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
I wrote:
> However, what I'm on about right at the moment is that I don't think
> there should be any delta in that test at all. As far as I can see,
> the design idea here is that multiranges will be automatically created
> over range types, and the user doesn't need to do that. To my mind,
> that means that they're an implementation detail and should not show up as
> separately-owned objects, any more than an autogenerated array type does.
Actually ... have you given any thought to just deciding that ranges and
multiranges are the same type? That is, any range can now potentially
contain multiple segments? That would eliminate a whole lot of the
tedious infrastructure hacking involved in this patch, and let you focus
on the actually-useful functionality.
It's possible that this is a bad idea. It bears a lot of similarity,
I guess, to the way that Postgres doesn't consider arrays of different
dimensionality to be distinct types. That has some advantages but it
surely also has downsides. I think on the whole the advantages win,
and I feel like that might also be the case here.
The gating requirement for this would be to make sure that a plain
range and a multirange can be told apart by contents. The first idea that
comes to mind is to repurpose the allegedly-unused RANGE_xB_NULL bits in
the flag byte at the end of the datum. If one of them is set, then it's a
multirange, and we use a different interpretation of the bytes between the
type OID and the flag byte.
Assuming that that's ok, it seems like we could consider the traditional
range functions like lower() and upper() to report on the first or last
range bound in a multirange --- essentially, they ignore any "holes"
that exist inside the range. And the new functions for multiranges
act much like array slicing, in that they give you back pieces of a range
that aren't actually of a distinct type.
regards, tom lane
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
@ 2020-03-07 21:20 ` Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:26 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
3 siblings, 1 reply; 152+ messages in thread
From: Tom Lane @ 2020-03-07 21:20 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
I wrote:
> Actually ... have you given any thought to just deciding that ranges and
> multiranges are the same type? That is, any range can now potentially
> contain multiple segments? That would eliminate a whole lot of the
> tedious infrastructure hacking involved in this patch, and let you focus
> on the actually-useful functionality.
Also, this would allow us to remove at least one ugly misfeature:
regression=# select '[1,2]'::int4range + '[3,10)'::int4range;
?column?
----------
[1,10)
(1 row)
regression=# select '[1,2]'::int4range + '[4,10)'::int4range;
ERROR: result of range union would not be contiguous
If the result of range_union can be a multirange as easily as not,
we would no longer have to throw an error here.
regards, tom lane
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:20 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
@ 2020-03-07 21:26 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-03-08 05:27 ` Re: range_agg Isaac Morland <isaac.morland@gmail.com>
0 siblings, 1 reply; 152+ messages in thread
From: Pavel Stehule @ 2020-03-07 21:26 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Paul A Jungwirth <pj@illuminatedcomputing.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
so 7. 3. 2020 v 22:20 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
> I wrote:
> > Actually ... have you given any thought to just deciding that ranges and
> > multiranges are the same type? That is, any range can now potentially
> > contain multiple segments? That would eliminate a whole lot of the
> > tedious infrastructure hacking involved in this patch, and let you focus
> > on the actually-useful functionality.
>
> Also, this would allow us to remove at least one ugly misfeature:
>
> regression=# select '[1,2]'::int4range + '[3,10)'::int4range;
> ?column?
> ----------
> [1,10)
> (1 row)
>
> regression=# select '[1,2]'::int4range + '[4,10)'::int4range;
> ERROR: result of range union would not be contiguous
>
> If the result of range_union can be a multirange as easily as not,
> we would no longer have to throw an error here.
>
I think this behave is correct. Sometimes you should to get only one range
- and this check is a protection against not continuous range.
if you expect multirange, then do
select '[1,2]'::int4range::multirange + '[4,10)'::int4range;
Regards
Pavel
>
> regards, tom lane
>
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:20 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:26 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
@ 2020-03-08 05:27 ` Isaac Morland <isaac.morland@gmail.com>
2020-03-08 16:56 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
0 siblings, 1 reply; 152+ messages in thread
From: Isaac Morland @ 2020-03-08 05:27 UTC (permalink / raw)
To: Pavel Stehule <pavel.stehule@gmail.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Paul A Jungwirth <pj@illuminatedcomputing.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Sat, 7 Mar 2020 at 16:27, Pavel Stehule <pavel.stehule@gmail.com> wrote:
>
> so 7. 3. 2020 v 22:20 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
>
>> I wrote:
>> > Actually ... have you given any thought to just deciding that ranges and
>> > multiranges are the same type? That is, any range can now potentially
>> > contain multiple segments? That would eliminate a whole lot of the
>> > tedious infrastructure hacking involved in this patch, and let you focus
>> > on the actually-useful functionality.
>>
>>
> I think this behave is correct. Sometimes you should to get only one range
> - and this check is a protection against not continuous range.
>
> if you expect multirange, then do
>
> select '[1,2]'::int4range::multirange + '[4,10)'::int4range;
>
Definitely agreed that range and multirange (or whatever it's called)
should be different. In the work I do I have a number of uses for ranges,
but not (yet) for multiranges. I want to be able to declare a column as
range and be sure that it is just a single range, and then call lower() and
upper() on it and be sure to get just one value in each case; and if I
accidentally try to take the union of ranges where the union isn’t another
range, I want to get an error rather than calculate some weird (in my
context) multirange.
On a related note, I was thinking about this and I don’t think I like
range_agg as a name at all. I know we have array_agg and string_agg but
surely shouldn’t this be called union_agg, and shouldn’t there also be an
intersect_agg? I mean, taking the union isn’t the only possible aggregate
on ranges or multiranges.
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:20 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:26 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-03-08 05:27 ` Re: range_agg Isaac Morland <isaac.morland@gmail.com>
@ 2020-03-08 16:56 ` Tom Lane <tgl@sss.pgh.pa.us>
0 siblings, 0 replies; 152+ messages in thread
From: Tom Lane @ 2020-03-08 16:56 UTC (permalink / raw)
To: Isaac Morland <isaac.morland@gmail.com>; +Cc: Pavel Stehule <pavel.stehule@gmail.com>; Paul A Jungwirth <pj@illuminatedcomputing.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
Isaac Morland <isaac.morland@gmail.com> writes:
>> so 7. 3. 2020 v 22:20 odesílatel Tom Lane <tgl@sss.pgh.pa.us> napsal:
>>> Actually ... have you given any thought to just deciding that ranges and
>>> multiranges are the same type? That is, any range can now potentially
>>> contain multiple segments?
> Definitely agreed that range and multirange (or whatever it's called)
> should be different. In the work I do I have a number of uses for ranges,
> but not (yet) for multiranges. I want to be able to declare a column as
> range and be sure that it is just a single range, and then call lower() and
> upper() on it and be sure to get just one value in each case; and if I
> accidentally try to take the union of ranges where the union isn’t another
> range, I want to get an error rather than calculate some weird (in my
> context) multirange.
I do not find that argument convincing at all. Surely you could put
that constraint on your column using "CHECK (numranges(VALUE) <= 1)"
or some such notation.
Also, you're attacking a straw man with respect to lower() and upper();
I did not suggest changing them to return arrays, but rather interpreting
them as returning the lowest or highest endpoint, which I think would be
transparent in most cases. (There would obviously need to be some other
functions that could dissect a multirange more completely.)
The real problem with the proposal as it stands, I think, is exactly
that range union has failure conditions and you have to use some other
operator if you want to get a successful result always. That's an
enormously ugly kluge, and if we'd done it right the first time nobody
would have objected.
Bottom line is that I don't think that we should add a pile of new moving
parts to the type system just because people are afraid of change;
arguably, that's *more* change (and more risk of bugs), not less.
Unifying the types would, for example, get rid of the pesky question
of what promoting a range to multirange should look like exactly,
because it'd be a no-op.
regards, tom lane
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
@ 2020-03-07 22:13 ` David Fetter <david@fetter.org>
2020-03-07 23:45 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
3 siblings, 1 reply; 152+ messages in thread
From: David Fetter @ 2020-03-07 22:13 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Paul A Jungwirth <pj@illuminatedcomputing.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Sat, Mar 07, 2020 at 04:06:32PM -0500, Tom Lane wrote:
> I wrote:
> > However, what I'm on about right at the moment is that I don't think
> > there should be any delta in that test at all. As far as I can see,
> > the design idea here is that multiranges will be automatically created
> > over range types, and the user doesn't need to do that. To my mind,
> > that means that they're an implementation detail and should not show up as
> > separately-owned objects, any more than an autogenerated array type does.
>
> Actually ... have you given any thought to just deciding that ranges and
> multiranges are the same type? That is, any range can now potentially
> contain multiple segments? That would eliminate a whole lot of the
> tedious infrastructure hacking involved in this patch, and let you focus
> on the actually-useful functionality.
If we're changing range types rather than constructing a new
multi-range layer atop them, I think it would be helpful to have some
way to figure out quickly whether this new range type was contiguous.
One way to do that would be to include a "range cardinality" in the
data structure which be the number of left ends in it.
One of the things I'd pictured doing with multiranges was along the
lines of a "full coverage" constraint like "During a shift, there can
be no interval that's not covered," which would correspond to a "range
cardinality" of 1.
I confess I'm getting a little twitchy about the idea of eliding the
cases of "one" and "many", though.
> Assuming that that's ok, it seems like we could consider the traditional
> range functions like lower() and upper() to report on the first or last
> range bound in a multirange --- essentially, they ignore any "holes"
> that exist inside the range. And the new functions for multiranges
> act much like array slicing, in that they give you back pieces of a range
> that aren't actually of a distinct type.
So new functions along the lines of lowers(), uppers(), opennesses(),
etc.? I guess this could be extended as needs emerge.
There's another use case not yet covered here that could make this
even more complex, we should probably plan for it: multi-ranges with
weights.
For example,
SELECT weighted_range_union(r)
FROM (VALUES('[0,1)'::float8range), ('[0,3)'), '('[2,5)')) AS t(r)
would yield something along the lines of:
(([0,1),1), ([1,3),2), ([3,5),1))
and wedging that into the range type seems messy. Each range would
then have a cardinality, and each range within would have a weight,
all of which would be an increasingly heavy burden on the common case
where there's just a single range.
Enhancing a separate multirange type to have weights seems like a
cleaner path forward.
Given that, I'm -1 on mushing multi-ranges into a special case of
ranges, or /vice versa/.
Best,
David.
--
David Fetter <david(at)fetter(dot)org> http://fetter.org/
Phone: +1 415 235 3778
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 22:13 ` Re: range_agg David Fetter <david@fetter.org>
@ 2020-03-07 23:45 ` Tom Lane <tgl@sss.pgh.pa.us>
2020-03-08 02:45 ` Re: range_agg David Fetter <david@fetter.org>
0 siblings, 1 reply; 152+ messages in thread
From: Tom Lane @ 2020-03-07 23:45 UTC (permalink / raw)
To: David Fetter <david@fetter.org>; +Cc: Paul A Jungwirth <pj@illuminatedcomputing.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
David Fetter <david@fetter.org> writes:
> There's another use case not yet covered here that could make this
> even more complex, we should probably plan for it: multi-ranges with
> weights.
I'm inclined to reject that as completely out of scope. The core
argument for unifying multiranges with ranges, if you ask me, is
to make the data type closed under union. Weights are from some
other universe.
regards, tom lane
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 22:13 ` Re: range_agg David Fetter <david@fetter.org>
2020-03-07 23:45 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
@ 2020-03-08 02:45 ` David Fetter <david@fetter.org>
0 siblings, 0 replies; 152+ messages in thread
From: David Fetter @ 2020-03-08 02:45 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Paul A Jungwirth <pj@illuminatedcomputing.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Sat, Mar 07, 2020 at 06:45:44PM -0500, Tom Lane wrote:
> David Fetter <david@fetter.org> writes:
> > There's another use case not yet covered here that could make this
> > even more complex, we should probably plan for it: multi-ranges
> > with weights.
>
> I'm inclined to reject that as completely out of scope. The core
> argument for unifying multiranges with ranges, if you ask me, is to
> make the data type closed under union. Weights are from some other
> universe.
I don't think they are. SQL databases are super useful because they do
bags in addition to sets, so set union isn't the only, or maybe even
the most important, operation over which ranges ought to be closed.
Best,
David.
--
David Fetter <david(at)fetter(dot)org> http://fetter.org/
Phone: +1 415 235 3778
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
@ 2020-03-09 17:53 ` Robert Haas <robertmhaas@gmail.com>
3 siblings, 0 replies; 152+ messages in thread
From: Robert Haas @ 2020-03-09 17:53 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Paul A Jungwirth <pj@illuminatedcomputing.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
On Sat, Mar 7, 2020 at 4:06 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> It's possible that this is a bad idea. It bears a lot of similarity,
> I guess, to the way that Postgres doesn't consider arrays of different
> dimensionality to be distinct types. That has some advantages but it
> surely also has downsides. I think on the whole the advantages win,
> and I feel like that might also be the case here.
Personally, I'm pretty unhappy with the fact that the array system
conflates arrays with different numbers of dimensions. Like, you end
up having to write array_upper(X, 1) instead of just array_upper(X),
and then you're still left wondering whether whatever you wrote is
going to blow up if somebody sneaks a multidimensional array in there,
or for that matter, an array with a non-standard lower bound. There's
lots of little things like that, where the decision to decorate the
array type with these extra frammishes makes it harder to use for
everybody even though most people don't use (or even want) those
features.
So count me as +1 for keeping range and multirange separate.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
@ 2020-03-10 01:34 ` Jeff Davis <pgsql@j-davis.com>
2020-03-10 16:08 ` Re: range_agg David Fetter <david@fetter.org>
3 siblings, 1 reply; 152+ messages in thread
From: Jeff Davis @ 2020-03-10 01:34 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Pavel Stehule <pavel.stehule@gmail.com>; pgsql-hackers
On Sat, 2020-03-07 at 16:06 -0500, Tom Lane wrote:
> Actually ... have you given any thought to just deciding that ranges
> and
> multiranges are the same type?
It has come up in a number of conversations, but I'm not sure if it was
discussed on this list.
> I think on the whole the advantages win,
> and I feel like that might also be the case here.
Some things to think about:
1. Ranges are common -- at least implicitly -- in a lot of
applications/systems. It's pretty easy to represent extrernal data as
ranges in postgres, and also to represent postgres ranges in external
systems. But I can see multiranges causing friction around a lot of
common tasks, like displaying in a UI. If you only expect ranges, you
can add a CHECK constraint, so this is annoying but not necessarily a
deal-breaker.
2. There are existing client libraries[1] that support range types and
transform them to types within the host language. Obviously, those
would need to be updated to expect multiple ranges.
3. It seems like we would want some kind of base "range" type. When you
try to break a multirange down into constituent ranges, what type would
those pieces be? (Aside: how do you get the constituent ranges?)
I'm thinking more about casting to see if there's a possible compromise
there.
Regards,
Jeff Davis
[1]
https://sfackler.github.io/rust-postgres-range/doc/v0.8.2/postgres_range/
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-10 01:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
@ 2020-03-10 16:08 ` David Fetter <david@fetter.org>
2020-03-11 23:04 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: David Fetter @ 2020-03-10 16:08 UTC (permalink / raw)
To: Jeff Davis <pgsql@j-davis.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; Paul A Jungwirth <pj@illuminatedcomputing.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; Pavel Stehule <pavel.stehule@gmail.com>; pgsql-hackers
On Mon, Mar 09, 2020 at 06:34:04PM -0700, Jeff Davis wrote:
> On Sat, 2020-03-07 at 16:06 -0500, Tom Lane wrote:
> > Actually ... have you given any thought to just deciding that ranges
> > and
> > multiranges are the same type?
>
> It has come up in a number of conversations, but I'm not sure if it was
> discussed on this list.
>
> > I think on the whole the advantages win,
> > and I feel like that might also be the case here.
>
> Some things to think about:
>
> 1. Ranges are common -- at least implicitly -- in a lot of
> applications/systems. It's pretty easy to represent extrernal data as
> ranges in postgres, and also to represent postgres ranges in external
> systems. But I can see multiranges causing friction around a lot of
> common tasks, like displaying in a UI. If you only expect ranges, you
> can add a CHECK constraint, so this is annoying but not necessarily a
> deal-breaker.
It could become well and truly burdensome in a UI or an API. The
difference between one, as ranges are now, and many, as multi-ranges
would be if we shoehorn them into the range type, are pretty annoying
to deal with.
> 2. There are existing client libraries[1] that support range types and
> transform them to types within the host language. Obviously, those
> would need to be updated to expect multiple ranges.
The type systems that would support such types might get unhappy with
us if we started messing with some of the properties like
contiguousness.
> 3. It seems like we would want some kind of base "range" type. When you
> try to break a multirange down into constituent ranges, what type would
> those pieces be? (Aside: how do you get the constituent ranges?)
>
> I'm thinking more about casting to see if there's a possible compromise
> there.
I think the right compromise is to recognize that the closure of a set
(ranges) over an operation (set union) may well be a different set
(multi-ranges). Other operations have already been proposed, complete
with concrete use cases that could really make PostgreSQL stand out.
That we don't have an obvious choice of "most correct" operation over
which to close ranges makes it even bigger a potential foot-gun
when we choose one arbitrarily and declare it to be the canonical one.
Best,
David.
--
David Fetter <david(at)fetter(dot)org> http://fetter.org/
Phone: +1 415 235 3778
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-10 01:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2020-03-10 16:08 ` Re: range_agg David Fetter <david@fetter.org>
@ 2020-03-11 23:04 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-12 12:38 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2020-03-11 23:04 UTC (permalink / raw)
To: David Fetter <david@fetter.org>; +Cc: Jeff Davis <pgsql@j-davis.com>; Tom Lane <tgl@sss.pgh.pa.us>; Alvaro Herrera <alvherre@2ndquadrant.com>; Pavel Stehule <pavel.stehule@gmail.com>; pgsql-hackers
Thanks everyone for offering some thoughts on this!
Tom Lane <tgl@sss.pgh.pa.us> wrote:
> have you given any thought to just deciding that ranges and
> multiranges are the same type?
I can see how it might be nice to have just one type to think about.
Still I think keeping them separate makes sense. Other folks have
brought up several reasons already. Just to chime in:
Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Isaac Morland <isaac.morland@gmail.com> writes:
> > Definitely agreed that range and multirange (or whatever it's called)
> > should be different. In the work I do I have a number of uses for ranges,
> > but not (yet) for multiranges. I want to be able to declare a column as
> > range and be sure that it is just a single range, and then call lower() and
> > upper() on it and be sure to get just one value in each case; and if I
> > accidentally try to take the union of ranges where the union isn’t another
> > range, I want to get an error rather than calculate some weird (in my
> > context) multirange.
>
> I do not find that argument convincing at all. Surely you could put
> that constraint on your column using "CHECK (numranges(VALUE) <= 1)"
> or some such notation.
A check constraint works for columns, but there are other contexts
where you'd like to restrict things to just a contiguous range, e.g.
user-defined functions and intermediate results in queries. Basic
ranges seem a lot simpler to think about, so I can appreciate how
letting any range be a multirange adds a heavy cognitive burden. I
think a lot of people will share Isaac's opinion here.
Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Also, this would allow us to remove at least one ugly misfeature:
>
> regression=# select '[1,2]'::int4range + '[3,10)'::int4range;
> ?column?
> ----------
> [1,10)
> (1 row)
>
> regression=# select '[1,2]'::int4range + '[4,10)'::int4range;
> ERROR: result of range union would not be contiguous
Because of backwards compatibility we can't really change +/-/* not to
raise (right?), so if we joined ranges and multiranges we'd need to
add operators with a different name. I was calling those @+/@-/@*
before, but that was considered too unintuitive and undiscoverable.
Having two types lets us use the nicer operator names.
Tom Lane <tgl@sss.pgh.pa.us> wrote:
> it seems like we could consider the traditional
> range functions like lower() and upper() to report on the first or last
> range bound in a multirange
I tried to keep functions/operators similar, so already lower(mr) =
lower(r) and upper(mr) = upper(r).
I think *conceptually* it's good to make ranges & multiranges as
interchangable as possible, but that doesn't mean they have to be the
same type.
Adding multiranges-as-ranges also raises questions about their string
format. If a multirange is {[1,2), [4,5)} would you only print the
curly braces when there is more than one element?
I don't *think* allowing non-contiguous ranges would break how we use
them in GiST indexes or exclusion constraints, but maybe someone can
think of some problem I can't. It's one place to be wary anyway. At
the very least it would make those things slower I expect.
On a few other issues people have raised recently:
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> I wonder what's the point of multirange arrays. Is there a reason we
> create those?
We have arrays of everything else, so why not have them for
multiranges? We don't have to identify specific use cases here,
although I can see how you'd want to call array_agg/UNNEST on some
multiranges, e.g. (Actually I really want to add an UNNEST that
*takes* a multirange, but that could be a follow-on commit.) If
nothing else I think omitting arrays of multiranges would be a strange
irregularity in the type system.
David G. Johnston <david.g.johnston@gmail.com> wrote:
> In the tests there is:
>
> +select '{[a,a],[b,b]}'::textmultirange;
> + textmultirange
> +----------------
> + {[a,a],[b,b]}
> +(1 row)
> +
> +-- without canonicalization, we can't join these:
> +select '{[a,a], [b,b]}'::textmultirange;
> + textmultirange
> +----------------
> + {[a,a],[b,b]}
> +(1 row)
> +
>
> Aside from the comment they are identical so I'm confused as to why both tests exist - though I suspect it has to do with the fact that the expected result would be {[a,b]} since text is discrete.
Those tests are for basic string parsing (multirange_in), so one is
testing {A,B} and the other {A, B} (with a space after the comma).
(There are some tests right above those that also have blank spaces,
but they only output a single element in the multirange result.)
David G. Johnston <david.g.johnston@gmail.com> wrote:
> Also, the current patch set seems a bit undecided on whether it wants to be truly a multi-range or a range that can report non-contiguous components. Specifically,
>
> +select '{[a,d), [b,f]}'::textmultirange;
> + textmultirange
> +----------------
> + {[a,f]}
> +(1 row)
Without a canonicalization function, we can't know that [a,a] touches
[b,b], but we *can* know that [a,d) touches [b,f). Or even:
regression=# select '{[a,b), [b,b]}'::textmultirange;
textmultirange
----------------
{[a,b]}
(1 row)
So I'm joining ranges whenever we know they touch. I think this is
consistent with existing range operators, e.g.:
regression=# select '[a,a]'::textrange -|- '[b,b]';
?column?
----------
f
regression=# select '[a,b)'::textrange -|- '[b,b]';
?column?
----------
t
David G. Johnston <david.g.johnston@gmail.com> wrote:
> There is a an argument that a multi-range should output {[a,d),[b,f]}. IMO its arguable that a multi-range container should not try and reduce the number of contained ranges at all.
Automatically combining touching ranges seems very desirable to me,
and one of the motivations to building a multirange type instead of
just using an array of ranges. Mathematically {[1,2), [2,3)} is
equivalent to {[1,3)}, and merging the touching elements keeps things
easier to read/understand and faster. Ranges themselves have a
canonical form too. Not canonicalizing raises a lot of questions, like
when are two "equivalent" ranges equal? And when you compose
operators/functions, do you keep all the internally-introduced splits,
or somehow preserve only splits that were present in your top-level
inputs? If you really want a list of possibly-touching ranges, I would
use an array for that. Why even have a range_agg (instead of just
array_agg'ing the ranges) if you're not going to merge the inputs?
Isaac Morland <isaac.morland@gmail.com> writes:
> On a related note, I was thinking about this and I don’t think I like
> range_agg as a name at all. I know we have array_agg and string_agg but surely
> shouldn’t this be called union_agg, and shouldn’t there also be an
> intersect_agg? I mean, taking the union isn’t the only possible aggregate on
> ranges or multiranges.
The patch does include a range_intersect_agg already. Since there are
so many set-like things in SQL, I don't think the unqualified
union_agg/intersect_agg are appropriate to give to ranges alone. And
the existing ${type}_agg functions are all "additive": json_agg,
json_object_agg, array_agg, string_agg, xmlagg. So I think range_agg
is the least-surprising name for this behavior. I'm not even the first
person to call it that, as you can see from [1].
David Fetter <david@fetter.org> writes:
> One way to do that would be to include a "range cardinality" in the
> data structure which be the number of left ends in it.
I agree that is probably useful enough to add to this patch. I'll work on it.
David Fetter <david@fetter.org> writes:
> There's another use case not yet covered here that could make this
> even more complex, we should probably plan for it: multi-ranges with
> weights.
Several people have asked me about this. I think it would need to be a
separate type though, e.g. weighted_multirange. Personally I wouldn't
mind working on it eventually, but I don't think it needs to be part
of this initial patch. Possibly it could even be an extension. In lieu
of a real type you also have an array-of-ranges, which is what I
originally proposed range_agg to return.
Finally, I think I mentioned this a long time ago, but I'm still not
sure if this patch needs work around these things:
- gist opclass
- spgist opclass
- typanalyze
- selectivity
I'd love for a real Postgres expert to tell me "No, we can add that
later" or "Yes, you have to add that now." Even better if they can
offer some help, because I'm not sure I understand those areas well
enough to do it myself.
Thanks all,
Paul
[1] https://git.proteus-tech.com/open-source/django-postgres/blob/fa91cf9b43ce942e84b1a9be22f445f3515ca3...
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-10 01:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2020-03-10 16:08 ` Re: range_agg David Fetter <david@fetter.org>
2020-03-11 23:04 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-03-12 12:38 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-13 16:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Alvaro Herrera @ 2020-03-12 12:38 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: David Fetter <david@fetter.org>; Jeff Davis <pgsql@j-davis.com>; Tom Lane <tgl@sss.pgh.pa.us>; Pavel Stehule <pavel.stehule@gmail.com>; pgsql-hackers
Hello Paul, thanks for the thorough response to all these points.
Regarding the merge of multiranges with ranges, I had also thought of
that at some point and was leaning towards doing that, but after the
latest responses I think the arguments against it are sensible; and now
there's a clear majority for keeping them separate.
I'll be posting an updated version of the patch later today.
I was a bit scared bit this part:
On 2020-Mar-11, Paul A Jungwirth wrote:
> Finally, I think I mentioned this a long time ago, but I'm still not
> sure if this patch needs work around these things:
>
> - gist opclass
> - spgist opclass
> - typanalyze
> - selectivity
>
> I'd love for a real Postgres expert to tell me "No, we can add that
> later" or "Yes, you have to add that now."
While I think that the gist and spgist opclass are in the "very nice to
have but still optional" category, the other two items seem mandatory
(but I'm not 100% certain about that, TBH). I'm not sure we have time
to get those ready during this commitfest.
... thinking about gist+spgist, I think they could be written
identically to those for ranges, using the lowest (first) lower bound
and the higher (last) upper bound.
... thinking about selectivity, I think the way to write that is to
first compute the selectivity for the range across the first lower bound
and the last upper bound, and then subtract that for the "negative"
space between the contained ranges.
I have no immediate thoughts about typanalyze. I suppose it should be
somehow based on the implementation for ranges ... maybe a first-cut is
to construct fake ranges covering the whole multirange (as above) and
just use the ranges implementation (compute_range_stats).
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-10 01:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2020-03-10 16:08 ` Re: range_agg David Fetter <david@fetter.org>
2020-03-11 23:04 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-12 12:38 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-03-13 16:32 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 0 replies; 152+ messages in thread
From: Paul A Jungwirth @ 2020-03-13 16:32 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: David Fetter <david@fetter.org>; Jeff Davis <pgsql@j-davis.com>; Tom Lane <tgl@sss.pgh.pa.us>; Pavel Stehule <pavel.stehule@gmail.com>; pgsql-hackers
On Thu, Mar 12, 2020 at 5:38 AM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
> ... thinking about gist+spgist, I think they could be written
> identically to those for ranges, using the lowest (first) lower bound
> and the higher (last) upper bound.
>
> ... thinking about selectivity, I think the way to write that is to
> first compute the selectivity for the range across the first lower bound
> and the last upper bound, and then subtract that for the "negative"
> space between the contained ranges.
>
> I have no immediate thoughts about typanalyze. I suppose it should be
> somehow based on the implementation for ranges ... maybe a first-cut is
> to construct fake ranges covering the whole multirange (as above) and
> just use the ranges implementation (compute_range_stats).
Thanks, this is pretty much what I was thinking too, but I'm really
glad to have someone who knows better confirm it. I can get started on
these right away, and I'll let folks know if I need any help. When I
looked at this last fall there was a lot I didn't understand. More or
less using the existing ranges implementation should be a big help
though.
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
@ 2020-03-13 16:33 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
1 sibling, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2020-03-13 16:33 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
On Wed, Mar 11, 2020 at 4:39 PM Paul A Jungwirth
<pj@illuminatedcomputing.com> wrote:
>
> On Sat, Mar 7, 2020 at 12:20 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> > Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> > > [ v11 patches ]
> > The cfbot isn't too happy with this; it's getting differently-ordered
> > results than you apparently did for the list of owned objects in
> > dependency.out's DROP OWNED BY test. Not sure why that should be ---
> > it seems like af6550d34 should have ensured that there's only one
> > possible ordering.
>
> Oh, my last email left out the most important part. :-) Is this
> failure online somewhere so I can take a look at it and fix it?
Looks like I sent this just to Tom before. This is something I need to
fix, right?
Regards,
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-03-13 17:06 ` Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 21:39 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-14 18:13 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 2 replies; 152+ messages in thread
From: Tom Lane @ 2020-03-13 17:06 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
Paul A Jungwirth <pj@illuminatedcomputing.com> writes:
> On Wed, Mar 11, 2020 at 4:39 PM Paul A Jungwirth
> <pj@illuminatedcomputing.com> wrote:
>> Oh, my last email left out the most important part. :-) Is this
>> failure online somewhere so I can take a look at it and fix it?
Look for your patch(es) at
http://commitfest.cputube.org
Right now it's not even applying, presumably because Alvaro already
pushed some pieces, so you need to rebase. But when it was applying,
one or both of the test builds was failing.
regards, tom lane
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
@ 2020-03-13 21:39 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-14 17:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
1 sibling, 1 reply; 152+ messages in thread
From: Alvaro Herrera @ 2020-03-13 21:39 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Paul A Jungwirth <pj@illuminatedcomputing.com>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
On 2020-Mar-13, Tom Lane wrote:
> Right now it's not even applying, presumably because Alvaro already
> pushed some pieces, so you need to rebase. But when it was applying,
> one or both of the test builds was failing.
Here's the rebased version.
I just realized I didn't include the API change I proposed in
https://postgr.es/m/20200306200343.GA625@alvherre.pgsql ...
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
[text/x-diff] v12-0001-Paul-s-main-patch.patch (329.4K, ../../20200313213906.GA16075@alvherre.pgsql/2-v12-0001-Paul-s-main-patch.patch)
download | inline diff:
From f48d715059681286ae7a9d3d962f1f7fe3cd0c03 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 9 Mar 2020 17:20:04 -0300
Subject: [PATCH v12 1/5] Paul's main patch
with minor changes by Alvaro
---
doc/src/sgml/extend.sgml | 28 +-
doc/src/sgml/func.sgml | 326 ++-
doc/src/sgml/rangetypes.sgml | 47 +-
src/backend/catalog/pg_proc.c | 17 +-
src/backend/catalog/pg_range.c | 10 +-
src/backend/catalog/pg_type.c | 119 +-
src/backend/commands/typecmds.c | 314 ++-
src/backend/parser/parse_coerce.c | 309 ++-
src/backend/utils/adt/Makefile | 1 +
src/backend/utils/adt/multirangetypes.c | 2169 +++++++++++++++
src/backend/utils/adt/pg_upgrade_support.c | 22 +
src/backend/utils/adt/pseudotypes.c | 25 +
src/backend/utils/adt/rangetypes.c | 349 ++-
src/backend/utils/cache/lsyscache.c | 62 +-
src/backend/utils/cache/syscache.c | 12 +
src/backend/utils/cache/typcache.c | 98 +-
src/backend/utils/fmgr/funcapi.c | 290 +-
src/bin/pg_dump/pg_dump.c | 150 +-
src/bin/pg_dump/pg_dump.h | 1 +
src/include/access/tupmacs.h | 4 +-
src/include/catalog/binary_upgrade.h | 2 +
src/include/catalog/indexing.h | 3 +
src/include/catalog/pg_aggregate.dat | 11 +
src/include/catalog/pg_amop.dat | 22 +
src/include/catalog/pg_amproc.dat | 12 +-
src/include/catalog/pg_cast.dat | 13 +
src/include/catalog/pg_opclass.dat | 4 +
src/include/catalog/pg_operator.dat | 169 ++
src/include/catalog/pg_opfamily.dat | 4 +
src/include/catalog/pg_proc.dat | 271 ++
src/include/catalog/pg_range.dat | 15 +-
src/include/catalog/pg_range.h | 5 +-
src/include/catalog/pg_type.dat | 39 +
src/include/catalog/pg_type.h | 7 +-
src/include/commands/typecmds.h | 2 +
src/include/utils/lsyscache.h | 3 +
src/include/utils/multirangetypes.h | 103 +
src/include/utils/rangetypes.h | 29 +-
src/include/utils/syscache.h | 1 +
src/include/utils/typcache.h | 6 +
src/pl/plpgsql/src/pl_comp.c | 6 +
src/test/regress/expected/dependency.out | 1 +
src/test/regress/expected/hash_func.out | 13 +
src/test/regress/expected/multirangetypes.out | 2395 +++++++++++++++++
src/test/regress/expected/opr_sanity.out | 26 +-
src/test/regress/expected/rangetypes.out | 38 +-
src/test/regress/expected/sanity_check.out | 2 +
src/test/regress/expected/type_sanity.out | 46 +-
src/test/regress/parallel_schedule | 3 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/hash_func.sql | 10 +
src/test/regress/sql/multirangetypes.sql | 630 +++++
src/test/regress/sql/opr_sanity.sql | 18 +-
src/test/regress/sql/rangetypes.sql | 13 +
src/test/regress/sql/type_sanity.sql | 16 +-
55 files changed, 8000 insertions(+), 292 deletions(-)
create mode 100644 src/backend/utils/adt/multirangetypes.c
create mode 100644 src/include/utils/multirangetypes.h
create mode 100644 src/test/regress/expected/multirangetypes.out
create mode 100644 src/test/regress/sql/multirangetypes.sql
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 9ec1af780b..92a1a254ac 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -229,9 +229,9 @@
</indexterm>
<para>
- Five pseudo-types of special interest are <type>anyelement</type>,
+ Six pseudo-types of special interest are <type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
- and <type>anyrange</type>,
+ <type>anyrange</type>, and <type>anymultirange</type>.
which are collectively called <firstterm>polymorphic types</firstterm>.
Any function declared using these types is said to be
a <firstterm>polymorphic function</firstterm>. A polymorphic function can
@@ -250,15 +250,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type>, the actual range type in
- the <type>anyrange</type> positions must be a range whose subtype is
- the same type appearing in the <type>anyelement</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -267,6 +267,20 @@
be an enum type.
</para>
+ <para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type>, the actual range type in
+ the <type>anyrange</type> positions must be a range whose subtype is
+ the same type appearing in the <type>anyelement</type> positions.
+ The type <type>anymultirange</type> accepts a multirange
+ whose contents match the other polymorphic types.
+ That is it must hold ranges matching <type>anyrange</type>
+ and base type elements matching <type>anyelement</type> (if either of
+ those are present). If <type>anyarray</type> is present its elements
+ must match the base type of the <type>anyrange</type> and/or the base
+ type of the ranges in an <type>anymultirange</type>.
+ </para>
+
<para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 323366feb6..cfe2c23e65 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -13546,7 +13546,7 @@ SELECT NULLIF(value, '(none)') ...
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14017,12 +14017,14 @@ NULL baz</literallayout>(3 rows)</entry>
<title>Range Functions and Operators</title>
<para>
- See <xref linkend="rangetypes"/> for an overview of range types.
+ See <xref linkend="rangetypes"/> for an overview of range and multirange types.
</para>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14040,134 +14042,215 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
- <entry><literal>f</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">f</literallayout></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
- <entry><literal>[5,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)
+'{[5,10)}'::nummultirange + '{[15,20)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,20)
+{[5,10), [15,20)}</literallayout></entry>
</row>
<row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
- <entry><literal>[10,15)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)
+'{[5,15)}'::int8multirange * '{[10,20)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[10,15)
+{[10,15)}</literallayout></entry>
</row>
<row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
- <entry><literal>[5,10)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)
+'{[5,20)}'::int8multirange - '{[10,15)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,10)
+{[5,10), [15,20)}</literallayout></entry>
</row>
</tbody>
@@ -14185,19 +14268,31 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. There are separate operators for union and difference that take
+ multirange parameters and return a multirange, and they do not fail even if
+ their arguments are disjoint. So if you need a union or difference operation
+ for ranges that may be disjoint, you can avoid errors by first casting your
+ ranges to multiranges.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14246,6 +14341,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower(numrange(1.1,2.2))</literal></entry>
<entry><literal>1.1</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14257,6 +14363,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper(numrange(1.1,2.2))</literal></entry>
<entry><literal>2.2</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14268,6 +14385,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>isempty(numrange(1.1,2.2))</literal></entry>
<entry><literal>false</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14279,6 +14407,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower_inc(numrange(1.1,2.2))</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14290,6 +14429,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper_inc(numrange(1.1,2.2))</literal></entry>
<entry><literal>false</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14301,6 +14451,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower_inf('(,)'::daterange)</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14312,6 +14473,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper_inf('(,)'::daterange)</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14323,16 +14495,38 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
+ <function>multirange</function>(<type>anyrange</type>)
+ </literal>
+ </entry>
+ <entry><type>anymultirange</type></entry>
+ <entry>a multirange containing just the given range</entry>
+ <entry><literal>multirange('[1,2)'::int4range)</literal></entry>
+ <entry><literal>{[1,2)}</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -14651,6 +14845,44 @@ NULL baz</literallayout>(3 rows)</entry>
</entry>
</row>
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
<row>
<entry>
<indexterm>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index b75fb3a392..c8784bdce3 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -232,10 +239,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -267,6 +294,19 @@ SELECT int8range(1, 14, '(]');
-- Using NULL for either bound causes the range to be unbounded on that side.
SELECT numrange(NULL, 2.2);
+</programlisting>
+ </para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
</programlisting>
</para>
</sect2>
@@ -341,6 +381,11 @@ SELECT '[1.234, 5.678]'::floatrange;
function in this example.
</para>
+ <para>
+ When you define your own range you automatically get a corresponding
+ multirange type.
+ </para>
+
<para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 423fd79d94..fb375cd3bf 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -192,6 +192,7 @@ ProcedureCreate(const char *procedureName,
genericInParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericInParam = true;
anyrangeInParam = true;
break;
@@ -219,6 +220,7 @@ ProcedureCreate(const char *procedureName,
genericOutParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericOutParam = true;
anyrangeOutParam = true;
break;
@@ -231,10 +233,10 @@ ProcedureCreate(const char *procedureName,
/*
* Do not allow polymorphic return type unless at least one input argument
- * is polymorphic. ANYRANGE return type is even stricter: must have an
- * ANYRANGE input (since we can't deduce the specific range type from
- * ANYELEMENT). Also, do not allow return type INTERNAL unless at least
- * one input argument is INTERNAL.
+ * is polymorphic. ANYRANGE and ANYMULTIRANGE return types are even
+ * stricter: must have an ANYRANGE or ANYMULTIRANGE input (since we can't
+ * deduce the specific range type from ANYELEMENT). Also, do not allow
+ * return type INTERNAL unless at least one input argument is INTERNAL.
*/
if ((IsPolymorphicType(returnType) || genericOutParam)
&& !genericInParam)
@@ -243,12 +245,13 @@ ProcedureCreate(const char *procedureName,
errmsg("cannot determine result data type"),
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
- if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
- !anyrangeInParam)
+ if ((returnType == ANYRANGEOID ||
+ returnType == ANYMULTIRANGEOID ||
+ anyrangeOutParam) && !anyrangeInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
- errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
+ errdetail("A function returning \"anyrange\" or \"anymultirange\" must have at least one \"anyrange\" or \"anymultirange\" argument.")));
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
ereport(ERROR,
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index b5bc36c2bd..1217a6ddd0 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,12 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index cd56714968..c359f27c74 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -27,6 +27,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
@@ -36,6 +37,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
+ bool tryOriginal);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -809,31 +813,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
char *
makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
- char *arr = (char *) palloc(NAMEDATALEN);
- int namelen = strlen(typeName);
- int i;
+ char *arr;
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
-
- if (i >= NAMEDATALEN - 1)
+ arr = makeUniqueTypeName(typeName, typeNamespace, false);
+ if (arr == NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -904,3 +887,91 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char *buf;
+ char *mrname;
+ char *rangestr;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "_multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ char *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
+
+ buf = psprintf("%s%s%s", prefix, "multi", rangestr);
+ }
+ else
+ buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
+
+ /* clip it at NAMEDATALEN-1 bytes */
+ buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
+
+ mrname = makeUniqueTypeName(buf, typeNamespace, true);
+ if (mrname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mrname;
+}
+
+/*
+ * makeUniqueTypeName
+ * Generate a unique name for a prospective new type
+ *
+ * Given a typeName, return a new palloc'ed name by preprending underscores
+ * until a non-conflicting name results.
+ *
+ * If tryOriginal, first try with zero underscores.
+ */
+static char *
+makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
+{
+ int i;
+ int namelen;
+ char dest[NAMEDATALEN];
+
+ Assert(strlen(typeName) <= NAMEDATALEN - 1);
+
+ if (tryOriginal &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(typeName);
+
+ /*
+ * The idea is to prepend underscores as needed until we make a name that
+ * doesn't collide with anything ...
+ */
+ namelen = strlen(typeName);
+ for (i = 1; i < NAMEDATALEN - 1; i++)
+ {
+ dest[i - 1] = '_';
+ strlcpy(dest + i, typeName, NAMEDATALEN - i);
+ if (namelen + i >= NAMEDATALEN)
+ truncate_identifier(dest, NAMEDATALEN, false);
+
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(dest);
+ }
+
+ return NULL;
+}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 8891b1d564..dfb2edd3d0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -105,9 +105,14 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid,
+ Oid rangeArrayOid, Oid *castFuncOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -738,7 +743,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1279,6 +1285,11 @@ checkEnumOwner(HeapTuple tup)
/*
* DefineRange
* Registers a new range type.
+ *
+ * Perhaps it might be worthwhile to set pg_type.typelem to the base type,
+ * and likewise on multiranges to set it to the range type. But having a
+ * non-zero typelem is treated elsewhere as a synonym for being an array,
+ * and users might have queries with that same assumption.
*/
ObjectAddress
DefineRange(CreateRangeStmt *stmt)
@@ -1287,7 +1298,11 @@ DefineRange(CreateRangeStmt *stmt)
Oid typeNamespace;
Oid typoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1304,6 +1319,8 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
+ Oid castFuncOid;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1452,8 +1469,10 @@ DefineRange(CreateRangeStmt *stmt)
/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for ranges */
alignment = (subtypalign == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
- /* Allocate OID for array type */
+ /* Allocate OID for array type, its multirange, and its multirange array */
rangeArrayOid = AssignTypeArrayOid();
+ multirangeOid = AssignTypeMultirangeOid();
+ multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
address =
@@ -1491,9 +1510,46 @@ DefineRange(CreateRangeStmt *stmt)
Assert(typoid == InvalidOid || typoid == address.objectId);
typoid = address.objectId;
+ /* Create the multirange that goes with it */
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(multirangeOid, /* force assignment of this type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_RANGE, /* type-category (range type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ Assert(multirangeOid == mltrngaddress.objectId);
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
@@ -1534,8 +1590,53 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ multirangeOid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ multirangeOid, typoid, rangeArrayOid,
+ &castFuncOid);
+
+ /* Create cast from the range type to its multirange type */
+ CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1613,6 +1714,147 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ *
+ * Sets castFuncOid to the oid of the new constructor that can be used
+ * to cast from a range to a multirange.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
+ Oid *castFuncOid)
+{
+ ObjectAddress myself,
+ referenced;
+ oidvector *argtypes;
+ Datum allParamTypes;
+ ArrayType *allParameterTypes;
+ Datum paramModes;
+ ArrayType *parameterModes;
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ /* 0-arg constructor - for empty multiranges */
+ argtypes = buildoidvector(NULL, 0);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor0", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /*
+ * Make the constructor internally-dependent on the multirange type so
+ * that they go away silently when the type is dropped. Note that pg_dump
+ * depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+
+ /*
+ * 1-arg constructor - for casts
+ *
+ * In theory we shouldn't need both this and the vararg (n-arg) constructor,
+ * but having a separate 1-arg function lets us define casts against it.
+ */
+ argtypes = buildoidvector(&rangeOid, 1);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor1", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ true, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ *castFuncOid = myself.objectId;
+
+ /* n-arg constructor - vararg */
+ argtypes = buildoidvector(&rangeArrayOid, 1);
+ allParamTypes = ObjectIdGetDatum(rangeArrayOid);
+ allParameterTypes = construct_array(&allParamTypes,
+ 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
+ parameterModes = construct_array(¶mModes, 1, CHAROID,
+ 1, true, 'c');
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor2", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(allParameterTypes), /* allParameterTypes */
+ PointerGetDatum(parameterModes), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ pfree(allParameterTypes);
+ pfree(parameterModes);
+}
/*
* Find suitable I/O functions for a type.
@@ -2045,6 +2287,72 @@ AssignTypeArrayOid(void)
return type_array_oid;
}
+/*
+ * AssignTypeMultirangeOid
+ *
+ * Pre-assign the range type's multirange OID for use in pg_type.oid
+ */
+Oid
+AssignTypeMultirangeOid(void)
+{
+ Oid type_multirange_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
+
+ type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
+ binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_oid;
+}
+
+/*
+ * AssignTypeMultirangeArrayOid
+ *
+ * Pre-assign the range type's multirange array OID for use in pg_type.typarray
+ */
+Oid
+AssignTypeMultirangeArrayOid(void)
+{
+ Oid type_multirange_array_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
+
+ type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
+ binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_array_oid;
+}
+
/*-------------------------------------------------------------------
* DefineCompositeType
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 929f758ef4..27bfc4bf8c 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -187,18 +187,20 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
- targetTypeId == ANYRANGEOID)
+ targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call anyarray_in,
- * anyenum_in, or anyrange_in, which will produce an error. Also, if
- * what we have is a domain over array, enum, or range, we have to
- * relabel it to its base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * anyarray_in, anyenum_in, anyrange_in, or anymultirange_in, which
+ * will produce an error. Also, if what we have is a domain over
+ * array, enum, range, or multirange, we have to relabel it to its
+ * base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1481,6 +1483,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid array_typelem;
Oid range_typeid = InvalidOid;
Oid range_typelem;
+ Oid multirange_typeid = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
@@ -1527,6 +1531,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1577,6 +1590,41 @@ check_generic_type_consistency(const Oid *actual_arg_types,
/* otherwise, they better match */
return false;
}
+ else
+ range_typelem = InvalidOid; /* keep compiler quiet */
+ }
+ else
+ range_typelem = InvalidOid; /* keep compiler quiet */
+
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_range_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
}
if (have_anynonarray)
@@ -1680,13 +1728,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid array_typelem;
- Oid range_typelem;
+ Oid range_typelem = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
+ bool have_anyrange = (rettype == ANYRANGEOID);
+ bool have_anymultirange = (rettype == ANYMULTIRANGEOID);
/*
* Loop through the arguments to see if we have any that are polymorphic.
@@ -1744,7 +1796,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else if (decl_type == ANYRANGEOID)
{
- have_generics = true;
+ have_generics = have_anyrange = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
@@ -1762,6 +1814,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ have_generics = have_anymultirange = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
}
/*
@@ -1812,9 +1884,12 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- if (range_typeid == ANYRANGEOID && !have_anyelement)
+ if (range_typeid == ANYRANGEOID && !have_anyelement && !have_anymultirange)
{
- /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
+ /*
+ * Special case for ANYRANGE input: okay iff no ANYELEMENT or
+ * ANYMULTIRANGE
+ */
range_typelem = ANYELEMENTOID;
}
else
@@ -1846,6 +1921,71 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(range_typeid),
format_type_be(elem_typeid))));
}
+ else
+ range_typelem = InvalidOid;
+ }
+
+ /* Get the range type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ if (multirange_typeid == ANYMULTIRANGEOID && !have_anyrange && !have_anyelement)
+ {
+ /*
+ * Special case for ANYMULTIRANGE input: okay iff no ANYRANGE or
+ * ANYELEMENT
+ */
+ multirange_typelem = ANYRANGEOID;
+ }
+ else
+ {
+ multirange_typelem = get_range_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+ }
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ /*
+ * Infer the element type too if needed (if there is an ANYMULTIRANGE
+ * and ANYELEMENT, with no ANYRANGE).
+ */
+ if (!OidIsValid(elem_typeid))
+ {
+ elem_typeid = get_range_subtype(range_typeid);
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
}
if (!OidIsValid(elem_typeid))
@@ -1855,6 +1995,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -1927,6 +2068,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -1958,6 +2110,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
@@ -2006,8 +2174,7 @@ resolve_generic_type(Oid declared_type,
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
- context_declared_type == ANYENUMOID ||
- context_declared_type == ANYRANGEOID)
+ context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
@@ -2019,11 +2186,37 @@ resolve_generic_type(Oid declared_type,
format_type_be(context_actual_type))));
return array_typeid;
}
+ else if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid range_typelem = get_range_subtype(context_base_type);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_range_multirange_subtype(context_base_type);
+ Oid range_typelem = get_range_subtype(multirange_typelem);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
- declared_type == ANYENUMOID ||
- declared_type == ANYRANGEOID)
+ declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
@@ -2051,6 +2244,19 @@ resolve_generic_type(Oid declared_type,
"anyrange", format_type_be(context_base_type))));
return range_typelem;
}
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ /* Use the element type corresponding to actual type */
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_range_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
@@ -2059,6 +2265,74 @@ resolve_generic_type(Oid declared_type,
return context_actual_type;
}
}
+ else if (declared_type == ANYRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_range_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
+ else
+ {
+ /* We can't infer a range from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
+ else if (declared_type == ANYMULTIRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typeid = get_range_multirange(context_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return multirange_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else
+ {
+ /* We can't infer a multirange from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
else
{
/* declared_type isn't polymorphic, so return it as-is */
@@ -2173,6 +2447,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..7118dd0034 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..0c9afd5448
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,2169 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ Oid typiofunc; /* range type's I/O function */
+ Oid typioparam; /* range type's I/O parameter */
+ FmgrInfo proc; /* lookup result for typiofunc */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData * get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks either either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str = NULL;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */ ;
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **)
+ repalloc(ranges, range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(InputFunctionCall(&cache->proc, range_str_copy,
+ cache->typioparam, typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "Unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->proc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(
+ ReceiveFunctionCall(&cache->proc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range = RangeTypePGetDatum(ranges[i]);
+ uint32 range_len;
+ char *range_data;
+
+ range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
+ range_len = VARSIZE(range) - VARHDRSZ;
+ range_data = VARDATA(range);
+
+ pq_sendint32(buf, range_len);
+ pq_sendbytes(buf, range_data, range_len);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != rngtypid)
+ {
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(rngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", rngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &cache->typiofunc);
+
+ if (!OidIsValid(cache->typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(cache->typiofunc, &cache->proc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ RangeType *range;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ bytelen += MAXALIGN(VARSIZE(range));
+ }
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ memcpy(ptr, range, VARSIZE(range));
+ ptr += MAXALIGN(VARSIZE(range));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of any ranges you like into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ * Returns the number of slots actually used,
+ * which may be less than input_range_count but never more.
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType * multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor2(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR, (errmsg("Can't construct multirange with a multi-dimensional array")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL element")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Construct multirange value from a single range.
+ * It'd be nice if we could just use multirange_constructor2
+ * for this case, but we need a non-variadic single-arg function
+ * to let us define a CAST from a range to its multirange.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ RangeType *range;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+
+ range = PG_GETARG_RANGE_P(0);
+
+ /* Make sure the range type matches. */
+ rngtypid = RangeTypeGetOid(range);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 1, &range));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ ereport(ERROR, (errmsg("Zero-param multirange constructor shouldn't have arguments")));
+}
+
+
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+multirange_union(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
+}
+
+/* multirange minus */
+Datum
+multirange_minus(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+multirange_intersect(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+ /*-----------------------------------------------
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1,
+ * but one extra won't hurt.
+ *-----------------------------------------------
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType * mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges1[0], ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1, MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
+ return false;
+
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
+ return false;
+ }
+
+ /* All ranges in mr2 are satisfied */
+ return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 18f2ee8226..248ea7f44a 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -51,6 +51,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
+Datum
+binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
+binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_array_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
Datum
binary_upgrade_set_next_toast_pg_type_oid(PG_FUNCTION_ARGS)
{
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 4653fc33e6..c909c9dc8c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -184,6 +185,30 @@ anyrange_out(PG_FUNCTION_ARGS)
return range_out(fcinfo);
}
+/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
/*
* void_in - input routine for pseudo-type VOID.
*
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 01ad8bc240..9d1ca13e32 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -431,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -452,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -475,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -485,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -495,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -505,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -515,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -957,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -969,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -993,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1101,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1110,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1132,9 +1180,81 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
}
+/* range, range -> range, range functions */
+
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
+ {
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
+ }
+
+ return false;
+}
+
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
+}
+
+
/* Btree support */
/* btree comparator */
@@ -1144,12 +1264,6 @@ range_cmp(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
- RangeBound lower1,
- lower2;
- RangeBound upper1,
- upper2;
- bool empty1,
- empty2;
int cmp;
check_stack_depth(); /* recurses when subtype is a range type */
@@ -1160,6 +1274,28 @@ range_cmp(PG_FUNCTION_ARGS)
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+ cmp = range_cmp_internal(typcache, r1, r2);
+
+ PG_FREE_IF_COPY(r1, 0);
+ PG_FREE_IF_COPY(r2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
@@ -1177,10 +1313,7 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
- PG_FREE_IF_COPY(r1, 0);
- PG_FREE_IF_COPY(r2, 1);
-
- PG_RETURN_INT32(cmp);
+ return cmp;
}
/* inequality operators using the range_cmp function */
@@ -1218,13 +1351,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1363,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1404,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1433,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1471,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1769,6 +1916,21 @@ range_get_flags(const RangeType *range)
return *((char *) range + VARSIZE(range) - 1);
}
+/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
@@ -1937,6 +2099,45 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
b1->val, b2->val));
}
+/*
+ * Compares two ranges so we can qsort them.
+ * This expects that you give qsort a RangeType **,
+ * so the RangeTypes can be in diverse locations,
+ * as long as you have a list of pointers to them all.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
/*
* Build an empty range value of the type indicated by the typcache entry.
*/
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 27bbb58f56..c6c079c623 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2494,6 +2494,16 @@ type_is_range(Oid typid)
return (get_typtype(typid) == TYPTYPE_RANGE);
}
+/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
/*
* get_type_category_preferred
*
@@ -3149,7 +3159,7 @@ get_namespace_name_or_temp(Oid nspid)
return get_namespace_name(nspid);
}
-/* ---------- PG_RANGE CACHE ---------- */
+/* ---------- PG_RANGE CACHES ---------- */
/*
* get_range_subtype
@@ -3202,6 +3212,56 @@ get_range_collation(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngmultitypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_range_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_range_multirange_subtype(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGEMULTIRANGE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 53d9ddf159..7654331a59 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -651,6 +651,18 @@ static const struct cachedesc cacheinfo[] = {
},
64
},
+ {RangeRelationId, /* RANGEMULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngmultitypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+
{RangeRelationId, /* RANGETYPE */
RangeTypidIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 854f133f9b..160846cf10 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -286,6 +286,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -302,6 +303,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -553,8 +557,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -591,7 +595,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -616,7 +620,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -641,7 +645,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -693,6 +697,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -737,6 +748,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -816,6 +834,16 @@ lookup_type_cache(Oid type_id, int flags)
(void) lookup_type_cache(typentry->rngelemtype->type_id, 0);
}
+ /*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
/*
* If requested, get information about a domain type
*/
@@ -927,6 +955,22 @@ load_rangetype_info(TypeCacheEntry *typentry)
typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
}
+/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Oid rangetypeOid;
+
+ rangetypeOid = get_range_multirange_subtype(typentry->type_id);
+ if (!OidIsValid(rangetypeOid))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
/*
* load_domaintype_info --- helper routine to set up domain constraint info
@@ -1527,11 +1571,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1574,6 +1618,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 0201e4f8d3..b10cd99144 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -470,11 +470,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anynonarray = false;
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
Oid anycollation = InvalidOid;
int i;
@@ -500,12 +502,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
case ANYRANGEOID:
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_anymultirange_result = true;
+ break;
default:
break;
}
}
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/*
@@ -533,6 +538,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (!OidIsValid(anyrange_type))
anyrange_type = get_call_expr_argtype(call_expr, i);
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(anymultirange_type))
+ anymultirange_type = get_call_expr_argtype(call_expr, i);
+ break;
default:
break;
}
@@ -540,7 +549,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/* If nothing found, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -561,19 +570,131 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ rngtype = get_range_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ anymultirange_type = mltrngtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
/* Enforce ANYNONARRAY if needed */
if (have_anynonarray && type_is_array(anyelement_type))
@@ -640,6 +761,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
default:
break;
}
@@ -664,9 +793,11 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
int inargno;
int i;
@@ -725,6 +856,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ have_anymultirange_result = true;
+ else
+ {
+ if (!OidIsValid(anymultirange_type))
+ {
+ anymultirange_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anymultirange_type))
+ return false;
+ }
+ argtypes[i] = anymultirange_type;
+ }
+ break;
default:
break;
}
@@ -734,12 +880,12 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
/* Done? */
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/* If no input polymorphics, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -760,19 +906,132 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ rngtype = get_range_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ }
+ else
+ {
+ return false;
+ }
+ }
/* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */
@@ -792,6 +1051,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = anymultirange_type;
+ break;
default:
break;
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 28312e14ef..ba53ac13ba 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -271,7 +271,8 @@ static void dumpSearchPath(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type);
+ bool force_array_type,
+ bool include_multirange_type);
static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
static void binary_upgrade_set_pg_class_oids(Archive *fout,
@@ -1580,7 +1581,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4339,16 +4340,49 @@ append_depends_on_extension(Archive *fout,
}
}
+static Oid
+get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+{
+ /*
+ * If the old version didn't assign an array type, but the new version
+ * does, we must select an unused type OID to assign. This currently only
+ * happens for domains, when upgrading pre-v11 to v11 and up.
+ *
+ * Note: local state here is kind of ugly, but we must have some, since we
+ * mustn't choose the same unused OID more than once.
+ */
+ static Oid next_possible_free_oid = FirstNormalObjectId;
+ PGresult *res;
+ bool is_dup;
+
+ do
+ {
+ ++next_possible_free_oid;
+ printfPQExpBuffer(upgrade_query,
+ "SELECT EXISTS(SELECT 1 "
+ "FROM pg_catalog.pg_type "
+ "WHERE oid = '%u'::pg_catalog.oid);",
+ next_possible_free_oid);
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+ is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
+ PQclear(res);
+ } while (is_dup);
+
+ return next_possible_free_oid;
+}
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type)
+ bool force_array_type,
+ bool include_multirange_type)
{
PQExpBuffer upgrade_query = createPQExpBuffer();
PGresult *res;
Oid pg_type_array_oid;
+ Oid pg_type_multirange_oid;
+ Oid pg_type_multirange_array_oid;
appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
@@ -4369,33 +4403,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQclear(res);
if (!OidIsValid(pg_type_array_oid) && force_array_type)
- {
- /*
- * If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
- *
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
- */
- static Oid next_possible_free_oid = FirstNormalObjectId;
- bool is_dup;
-
- do
- {
- ++next_possible_free_oid;
- printfPQExpBuffer(upgrade_query,
- "SELECT EXISTS(SELECT 1 "
- "FROM pg_catalog.pg_type "
- "WHERE oid = '%u'::pg_catalog.oid);",
- next_possible_free_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
- is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
- PQclear(res);
- } while (is_dup);
-
- pg_type_array_oid = next_possible_free_oid;
- }
+ pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
if (OidIsValid(pg_type_array_oid))
{
@@ -4406,6 +4414,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
pg_type_array_oid);
}
+ /*
+ * Pre-set the multirange type oid and its own array type oid.
+ */
+ if (include_multirange_type)
+ {
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBuffer(upgrade_query,
+ "SELECT t.oid, t.typarray "
+ "FROM pg_catalog.pg_type t "
+ "JOIN pg_catalog.pg_range r "
+ "ON t.oid = r.rngmultitypid "
+ "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
+ pg_type_oid);
+
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+
+ pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+ pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
+
+ PQclear(res);
+ }
+ else
+ {
+ pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ }
+
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_oid);
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_array_oid);
+ }
+
destroyPQExpBuffer(upgrade_query);
}
@@ -4439,7 +4487,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
- pg_type_oid, false);
+ pg_type_oid, false, false);
if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
{
@@ -4988,6 +5036,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
@@ -8102,9 +8155,12 @@ getProcLangs(Archive *fout, int *numProcLangs)
/*
* getCasts
- * get basic information about every cast in the system
+ * get basic information about most casts in the system
*
* numCasts is set to the number of casts read in
+ *
+ * Skip casts from a range to its multirange, since we'll create those
+ * automatically.
*/
CastInfo *
getCasts(Archive *fout, int *numCasts)
@@ -8122,7 +8178,20 @@ getCasts(Archive *fout, int *numCasts)
int i_castcontext;
int i_castmethod;
- if (fout->remoteVersion >= 80400)
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBufferStr(query, "SELECT tableoid, oid, "
+ "castsource, casttarget, castfunc, castcontext, "
+ "castmethod "
+ "FROM pg_cast c "
+ "WHERE NOT EXISTS ( "
+ "SELECT 1 FROM pg_range r "
+ "WHERE c.castsource = r.rngtypid "
+ "AND c.casttarget = r.rngmultitypid "
+ ") "
+ "ORDER BY 3,4");
+ }
+ else if (fout->remoteVersion >= 80400)
{
appendPQExpBufferStr(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
@@ -10310,7 +10379,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
qualtypname);
@@ -10436,7 +10505,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, true);
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
qualtypname);
@@ -10542,7 +10611,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
qualtypname);
@@ -10747,7 +10816,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
@@ -10934,7 +11003,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- true); /* force array type */
+ true, /* force array type */
+ false); /* force multirange type */
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -11122,7 +11192,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
{
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
}
@@ -11396,7 +11466,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
stinfo->baseType->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
fmtQualifiedDumpable(stinfo));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index e0c6444ef6..90a93b4cb2 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -176,6 +176,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 70157cf90a..c262265b95 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -139,8 +139,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 12d94fe1b3..01d95117cd 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,6 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 8be303870f..566f9364c5 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -327,6 +327,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_rngmultitypid_index, 8001, on pg_range using btree(rngmultitypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index ffabe275c0..03bab74253 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..827ac4f03c 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 75c0152b66..cf0cac5641 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -285,6 +285,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
@@ -445,6 +447,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
@@ -1263,12 +1270,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index 6ef8b8a4e7..14277c8fdf 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -512,4 +512,17 @@
{ castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
castcontext => 'e', castmethod => 'f' },
+# range to multirange
+{ castsource => 'int4range', casttarget => 'int4multirange', castfunc => 'int4multirange(int4range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'int8range', casttarget => 'int8multirange', castfunc => 'int8multirange(int8range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'numrange', casttarget => 'nummultirange', castfunc => 'nummultirange(numrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'daterange', casttarget => 'datemultirange', castfunc => 'datemultirange(daterange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tsrange', casttarget => 'tsmultirange', castfunc => 'tsmultirange(tsrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tstzrange', casttarget => 'tstzmultirange', castfunc => 'tstzmultirange(tstzrange)',
+ castcontext => 'e', castmethod => 'f' },
]
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index ab2f50c9eb..eebebbcdb4 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 7c135da3b1..24f4a32203 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3297,5 +3297,174 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'contsel', oprjoin => 'contjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 26227df216..901fd7b303 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 7fb574f9dc..27f25d989b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9643,6 +9643,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9692,6 +9696,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9760,6 +9771,258 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8114',
+ proname => 'multirange_union', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union' },
+{ oid => '8120',
+ proname => 'multirange_minus', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus' },
+{ oid => '8126',
+ proname => 'multirange_intersect', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8146', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 't',
+ prorettype => 'int4multirange', proargtypes => 'int4range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8147', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 't',
+ prorettype => 'nummultirange', proargtypes => 'numrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8148', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 't',
+ prorettype => 'tsmultirange', proargtypes => 'tsrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8149', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 't',
+ prorettype => 'tstzmultirange', proargtypes => 'tstzrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8150', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 't',
+ prorettype => 'datemultirange', proargtypes => 'daterange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8151', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 't',
+ prorettype => 'int8multirange', proargtypes => 'int8range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8152', descr => 'anymultirange cast',
+ proname => 'multirange', proisstrict => 't',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
@@ -10142,6 +10405,14 @@
proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
+{ oid => '8144', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
+{ oid => '8145', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
{ oid => '3585', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index 479754c245..10060255c9 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ rngmultitypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', rngmultitypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', rngmultitypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', rngmultitypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ rngmultitypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ rngmultitypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index 98172bb1b6..60a8fde518 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -34,6 +34,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* OID of range's element type (subtype) */
Oid rngsubtype BKI_LOOKUP(pg_type);
+ /* OID of the range's multirange type */
+ Oid rngmultitypid BKI_LOOKUP(pg_type);
+
/* collation for this range type, or 0 */
Oid rngcollation BKI_DEFAULT(0);
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index b00597d6ff..6e1cb796f1 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -486,6 +486,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -590,5 +624,10 @@
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 97890946c5..d18f19f55a 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -263,6 +263,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -299,7 +300,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#endif /* EXPOSE_TO_CLIENT_CODE */
@@ -358,4 +360,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 0162bc2ffe..80e3a5e399 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
+extern Oid AssignTypeMultirangeOid(void);
+extern Oid AssignTypeMultirangeArrayOid(void);
extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 4e646c55e9..80410dcd04 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -155,6 +155,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -181,6 +182,8 @@ extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
extern Oid get_range_collation(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_range_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
extern bool get_index_isreplident(Oid index_oid);
extern bool get_index_isvalid(Oid index_oid);
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..8779bd1f6e
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ char vl_len_[4]; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType * mr1,
+ MultirangeType * mr2);
+extern MultirangeType * multirange_minus_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType * multirange_intersect_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType * range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType * make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType * make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 0cbbf09033..204aee4054 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
@@ -92,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -115,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -125,6 +141,7 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
@@ -132,8 +149,12 @@ extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f27b73d76d..e8f393520a 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -79,6 +79,7 @@ enum SysCacheIdentifier
PUBLICATIONOID,
PUBLICATIONREL,
PUBLICATIONRELMAP,
+ RANGEMULTIRANGE,
RANGETYPE,
RELNAMENSP,
RELOID,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index cdd20e56d7..f40a462b22 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -100,6 +100,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_canonical_finfo; /* canonicalization function, if any */
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
+ /*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype; /* underlying range type */
+
/*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
@@ -143,6 +148,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index c8e43e684f..2d59021d68 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -511,6 +511,8 @@ do_compile(FunctionCallInfo fcinfo,
rettypeid = INT4ARRAYOID;
else if (rettypeid == ANYRANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2087,6 +2089,7 @@ build_datatype(HeapTuple typeTup, int32 typmod,
case TYPTYPE_BASE:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
+ case TYPTYPE_MULTIRANGE:
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_COMPOSITE:
@@ -2501,6 +2504,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYRANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..778699a961 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -141,6 +141,7 @@ owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
owner of type deptest_range
+owner of type deptest_multirange
owner of table deptest2
owner of sequence ss1
owner of type deptest_t
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..723f2408b0
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,2395 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+ int4multirange
+----------------
+ {}
+(1 row)
+
+select int4range(1, 3)::int4multirange;
+ int4range
+-----------
+ {[1,3)}
+(1 row)
+
+select int4range(1, null)::int4multirange;
+ int4range
+-----------
+ {[1,)}
+(1 row)
+
+select int4range(null, null)::int4multirange;
+ int4range
+-----------
+ {(,)}
+(1 row)
+
+select 'empty'::textrange::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textrange('a', 'c')::textmultirange;
+ textrange
+-----------
+ {[a,c)}
+(1 row)
+
+select textrange('a', null)::textmultirange;
+ textrange
+-----------
+ {[a,)}
+(1 row)
+
+select textrange(null, null)::textmultirange;
+ textrange
+-----------
+ {(,)}
+(1 row)
+
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT nummultirange() + nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT nummultirange() - nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() * nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+ mr_polymorphic
+----------------
+ {[1,4)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 40468e8f49..1514f3af7c 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -206,6 +213,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -224,6 +233,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -329,13 +340,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
@@ -343,16 +355,19 @@ ORDER BY 2;
2502 | anyarray_recv
2312 | anyelement_in
3504 | anyenum_in
+ 8002 | anymultirange_in
2777 | anynonarray_in
3832 | anyrange_in
750 | array_in
2400 | array_recv
3506 | enum_in
3532 | enum_recv
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(13 rows)
+(16 rows)
-- Look for functions that accept cstring and are neither datatype input
-- functions nor encoding conversion functions. It's almost never a good
@@ -2139,13 +2154,14 @@ ORDER BY 1, 2, 3;
| float_ops | float4_ops | real
| float_ops | float8_ops | double precision
| jsonb_ops | jsonb_ops | jsonb
+ | multirange_ops | multirange_ops | anymultirange
| numeric_ops | numeric_ops | numeric
| range_ops | range_ops | anyrange
| record_image_ops | record_image_ops | record
| record_ops | record_ops | record
| tsquery_ops | tsquery_ops | tsquery
| tsvector_ops | tsvector_ops | tsvector
-(14 rows)
+(15 rows)
-- **************** pg_index ****************
-- Look for illegal values in pg_index fields.
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 220f2d96cb..6d00a3efab 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1371,7 +1389,7 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
@@ -1482,6 +1500,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1490,6 +1509,16 @@ select * from outparam_succeed(int4range(1,2));
[1,2) | foo
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1498,6 +1527,7 @@ select * from inoutparam_succeed(int4range(1,2));
2 | [1,2)
(1 row)
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
select * from table_succeed(123, int4range(1,11));
@@ -1510,14 +1540,14 @@ select * from table_succeed(123, int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 192445878d..d9ce961be2 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
numrange_test|t
onek|t
onek2|t
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index cd7fc03b04..60a9b4d22f 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -193,18 +193,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -238,17 +239,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -316,18 +318,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -361,17 +364,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -629,3 +633,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
+ rngtypid | rngsubtype | rngmultitypid
+----------+------------+---------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index d2b17dd3ea..5355da7b01 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index acba391332..1f677f63af 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..0f70919e1a
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,630 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+select int4range(1, 3)::int4multirange;
+select int4range(1, null)::int4multirange;
+select int4range(null, null)::int4multirange;
+select 'empty'::textrange::textmultirange;
+select textrange('a', 'c')::textmultirange;
+select textrange('a', null)::textmultirange;
+select textrange(null, null)::textmultirange;
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT nummultirange() + nummultirange();
+SELECT nummultirange() + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT nummultirange() - nummultirange();
+SELECT nummultirange() - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+SELECT nummultirange() * nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index f06f245db3..4110a56ad2 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -273,13 +284,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- Look for functions that accept cstring and are neither datatype input
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 72d80bc9d4..07c9bcf6d3 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -512,16 +516,25 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fed413bf98..2e34bc8b65 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -151,7 +151,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -180,7 +180,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -232,7 +232,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -261,7 +261,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -465,3 +465,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
--
2.20.1
[text/x-diff] v12-0002-match-a77315fdf2a197a925e670be2d8b376c4ac02efc.patch (7.2K, ../../20200313213906.GA16075@alvherre.pgsql/3-v12-0002-match-a77315fdf2a197a925e670be2d8b376c4ac02efc.patch)
download | inline diff:
From 83f022a920132acbbd4f0ccfa690e7a2cd8baa66 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 5 Mar 2020 12:04:46 -0300
Subject: [PATCH v12 2/5] match a77315fdf2a197a925e670be2d8b376c4ac02efc
---
src/backend/utils/adt/multirangetypes.c | 61 ++++++++++++-------------
src/backend/utils/adt/rangetypes.c | 2 +
src/include/utils/typcache.h | 2 +-
3 files changed, 31 insertions(+), 34 deletions(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 0c9afd5448..4d45aa581b 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -40,10 +40,9 @@
typedef struct MultirangeIOData
{
TypeCacheEntry *typcache; /* multirange type's typcache entry */
- Oid typiofunc; /* range type's I/O function */
+ FmgrInfo typioproc; /* range type's I/O proc */
Oid typioparam; /* range type's I/O parameter */
- FmgrInfo proc; /* lookup result for typiofunc */
-} MultirangeIOData;
+} MultirangeIOData;
typedef enum
{
@@ -54,10 +53,10 @@ typedef enum
MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
MULTIRANGE_AFTER_RANGE,
MULTIRANGE_FINISHED,
-} MultirangeParseState;
+} MultirangeParseState;
-static MultirangeIOData * get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
- IOFuncSelector func);
+static MultirangeIOData *get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
+ IOFuncSelector func);
static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
RangeType **ranges);
@@ -178,8 +177,10 @@ multirange_in(PG_FUNCTION_ARGS)
repalloc(ranges, range_capacity * sizeof(RangeType *));
}
ranges_seen++;
- range = DatumGetRangeTypeP(InputFunctionCall(&cache->proc, range_str_copy,
- cache->typioparam, typmod));
+ range = DatumGetRangeTypeP(InputFunctionCall(&cache->typioproc,
+ range_str_copy,
+ cache->typioparam,
+ typmod));
if (!RangeIsEmpty(range))
ranges[range_count++] = range;
parse_state = MULTIRANGE_AFTER_RANGE;
@@ -266,7 +267,7 @@ multirange_out(PG_FUNCTION_ARGS)
if (range_count > 0)
appendStringInfoChar(&buf, ',');
range = (RangeType *) ptr;
- rangeStr = OutputFunctionCall(&cache->proc, RangeTypePGetDatum(range));
+ rangeStr = OutputFunctionCall(&cache->typioproc, RangeTypePGetDatum(range));
appendStringInfoString(&buf, rangeStr);
ptr += MAXALIGN(VARSIZE(range));
range_count++;
@@ -311,8 +312,7 @@ multirange_recv(PG_FUNCTION_ARGS)
initStringInfo(&range_buf);
appendBinaryStringInfo(&range_buf, range_data, range_len);
- ranges[i] = DatumGetRangeTypeP(
- ReceiveFunctionCall(&cache->proc,
+ ranges[i] = DatumGetRangeTypeP(ReceiveFunctionCall(&cache->typioproc,
&range_buf,
cache->typioparam,
typmod));
@@ -346,16 +346,13 @@ multirange_send(PG_FUNCTION_ARGS)
multirange_deserialize(multirange, &range_count, &ranges);
for (i = 0; i < range_count; i++)
{
- Datum range = RangeTypePGetDatum(ranges[i]);
- uint32 range_len;
- char *range_data;
+ Datum range;
- range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
- range_len = VARSIZE(range) - VARHDRSZ;
- range_data = VARDATA(range);
+ range = RangeTypePGetDatum(ranges[i]);
+ range = PointerGetDatum(SendFunctionCall(&cache->typioproc, range));
- pq_sendint32(buf, range_len);
- pq_sendbytes(buf, range_data, range_len);
+ pq_sendint32(buf, VARSIZE(range) - VARHDRSZ);
+ pq_sendbytes(buf, VARDATA(range), VARSIZE(range) - VARHDRSZ);
}
PG_RETURN_BYTEA_P(pq_endtypsend(buf));
@@ -381,6 +378,7 @@ get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid, IOFuncSelector fun
if (cache == NULL || cache->typcache->type_id != rngtypid)
{
+ Oid typiofunc;
int16 typlen;
bool typbyval;
char typalign;
@@ -400,9 +398,9 @@ get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid, IOFuncSelector fun
&typalign,
&typdelim,
&cache->typioparam,
- &cache->typiofunc);
+ &typiofunc);
- if (!OidIsValid(cache->typiofunc))
+ if (!OidIsValid(typiofunc))
{
/* this could only happen for receive or send */
if (func == IOFunc_receive)
@@ -416,7 +414,7 @@ get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid, IOFuncSelector fun
errmsg("no binary output function available for type %s",
format_type_be(cache->typcache->rngtype->type_id))));
}
- fmgr_info_cxt(cache->typiofunc, &cache->proc,
+ fmgr_info_cxt(typiofunc, &cache->typioproc,
fcinfo->flinfo->fn_mcxt);
fcinfo->flinfo->fn_extra = (void *) cache;
@@ -473,7 +471,6 @@ make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
RangeType **ranges)
{
MultirangeType *multirange;
- RangeType *range;
int i;
int32 bytelen;
Pointer ptr;
@@ -489,10 +486,7 @@ make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
/* Count space for all ranges */
for (i = 0; i < range_count; i++)
- {
- range = ranges[i];
- bytelen += MAXALIGN(VARSIZE(range));
- }
+ bytelen += MAXALIGN(VARSIZE(ranges[i]));
/* Note: zero-fill is required here, just as in heap tuples */
multirange = palloc0(bytelen);
@@ -505,19 +499,20 @@ make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
ptr = (char *) MAXALIGN(multirange + 1);
for (i = 0; i < range_count; i++)
{
- range = ranges[i];
- memcpy(ptr, range, VARSIZE(range));
- ptr += MAXALIGN(VARSIZE(range));
+ memcpy(ptr, ranges[i], VARSIZE(ranges[i]));
+ ptr += MAXALIGN(VARSIZE(ranges[i]));
}
return multirange;
}
/*
- * Converts a list of any ranges you like into a list that is sorted and merged.
+ * Converts a list of arbitrary ranges into a list that is sorted and merged.
* Changes the contents of `ranges`.
- * Returns the number of slots actually used,
- * which may be less than input_range_count but never more.
+ *
+ * Returns the number of slots actually used, which may be less than
+ * input_range_count but never more.
+ *
* We assume that no input ranges are null, but empties are okay.
*/
static int32
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 9d1ca13e32..7077bb0309 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -2100,6 +2100,8 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
}
/*
+ * qsort callback for sorting ranges.
+ *
* Compares two ranges so we can qsort them.
* This expects that you give qsort a RangeType **,
* so the RangeTypes can be in diverse locations,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index f40a462b22..33f332a141 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -103,7 +103,7 @@ typedef struct TypeCacheEntry
/*
* Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
*/
- struct TypeCacheEntry *rngtype; /* underlying range type */
+ struct TypeCacheEntry *rngtype; /* multirange's range underlying type */
/*
* Domain's base type and typmod if it's a domain type. Zeroes if not
--
2.20.1
[text/x-diff] v12-0003-whitespace-varlena-struct-change-comment-on-qsor.patch (3.1K, ../../20200313213906.GA16075@alvherre.pgsql/4-v12-0003-whitespace-varlena-struct-change-comment-on-qsor.patch)
download | inline diff:
From dfc480de3c3e16219bc9f177d1b0edbedb90fc91 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 6 Mar 2020 19:03:25 -0300
Subject: [PATCH v12 3/5] whitespace, varlena struct change, comment on qsort
cb
---
src/backend/utils/adt/multirangetypes.c | 11 ++++++++---
src/backend/utils/adt/rangetypes.c | 7 +++----
src/include/utils/multirangetypes.h | 2 +-
3 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 4d45aa581b..a7bc2336b2 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -1605,7 +1605,8 @@ multirange_overleft_range(PG_FUNCTION_ARGS)
multirange_deserialize(mr, &range_count, &ranges);
- PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges[range_count - 1], r));
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges[range_count - 1], r));
}
Datum
@@ -1627,7 +1628,9 @@ multirange_overleft_multirange(PG_FUNCTION_ARGS)
multirange_deserialize(mr1, &range_count1, &ranges1);
multirange_deserialize(mr2, &range_count2, &ranges2);
- PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[range_count2 - 1]));
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges1[range_count1 - 1],
+ ranges2[range_count2 - 1]));
}
/* does not extend to left of? */
@@ -1688,7 +1691,9 @@ multirange_overright_multirange(PG_FUNCTION_ARGS)
multirange_deserialize(mr1, &range_count1, &ranges1);
multirange_deserialize(mr2, &range_count2, &ranges2);
- PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges1[0], ranges2[0]));
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype,
+ ranges1[0],
+ ranges2[0]));
}
/* contains? */
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 7077bb0309..0af3c67296 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -2102,10 +2102,9 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
/*
* qsort callback for sorting ranges.
*
- * Compares two ranges so we can qsort them.
- * This expects that you give qsort a RangeType **,
- * so the RangeTypes can be in diverse locations,
- * as long as you have a list of pointers to them all.
+ * Two empty ranges compare equal; an empty range sorts to the left of any
+ * non-empty range. Two non-empty ranges are sorted by lower bound first
+ * and by upper bound next.
*/
int
range_compare(const void *key1, const void *key2, void *arg)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
index 8779bd1f6e..98a8878ca0 100644
--- a/src/include/utils/multirangetypes.h
+++ b/src/include/utils/multirangetypes.h
@@ -24,7 +24,7 @@
*/
typedef struct
{
- char vl_len_[4]; /* varlena header (do not touch directly!) */
+ int32 vl_len_; /* varlena header (do not touch directly!) */
Oid multirangetypid; /* multirange type's own OID */
uint32 rangeCount; /* the number of ranges */
--
2.20.1
[text/x-diff] v12-0004-Fix-ereport-elog-calls.patch (4.1K, ../../20200313213906.GA16075@alvherre.pgsql/5-v12-0004-Fix-ereport-elog-calls.patch)
download | inline diff:
From 59d7e4c2a677225d4d609e8016ba316166fd291c Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 9 Mar 2020 16:22:46 -0300
Subject: [PATCH v12 4/5] Fix ereport/elog calls
---
src/backend/utils/adt/multirangetypes.c | 37 ++++++++++++++++++-------
1 file changed, 27 insertions(+), 10 deletions(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index a7bc2336b2..e58ab5baf3 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -223,7 +223,7 @@ multirange_in(PG_FUNCTION_ARGS)
parse_state = MULTIRANGE_IN_RANGE_QUOTED;
break;
default:
- elog(ERROR, "Unknown parse state: %d", parse_state);
+ elog(ERROR, "unknown parse state: %d", parse_state);
}
}
@@ -656,17 +656,23 @@ multirange_constructor2(PG_FUNCTION_ARGS)
*/
if (PG_ARGISNULL(0))
- ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
rangeArray = PG_GETARG_ARRAYTYPE_P(0);
dims = ARR_NDIM(rangeArray);
if (dims > 1)
- ereport(ERROR, (errmsg("Can't construct multirange with a multi-dimensional array")));
+ ereport(ERROR,
+ (errcode(ERRCODE_CARDINALITY_VIOLATION),
+ errmsg("multiranges cannot be constructed from multi-dimensional arrays")));
rngtypid = ARR_ELEMTYPE(rangeArray);
if (rngtypid != rangetyp->type_id)
- ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
/*
* Be careful: we can still be called with zero ranges, like this:
@@ -686,7 +692,9 @@ multirange_constructor2(PG_FUNCTION_ARGS)
for (i = 0; i < range_count; i++)
{
if (nulls[i])
- ereport(ERROR, (errmsg("Can't construct multirange with a NULL element")));
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
/* make_multirange will do its own copy */
ranges[i] = DatumGetRangeTypeP(elements[i]);
@@ -720,14 +728,18 @@ multirange_constructor1(PG_FUNCTION_ARGS)
*/
if (PG_ARGISNULL(0))
- ereport(ERROR, (errmsg("Can't construct multirange with a NULL input")));
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
range = PG_GETARG_RANGE_P(0);
/* Make sure the range type matches. */
rngtypid = RangeTypeGetOid(range);
if (rngtypid != rangetyp->type_id)
- ereport(ERROR, (errmsg("type %u does not match constructor type", rngtypid)));
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 1, &range));
}
@@ -752,7 +764,8 @@ multirange_constructor0(PG_FUNCTION_ARGS)
if (PG_NARGS() == 0)
PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
else
- ereport(ERROR, (errmsg("Zero-param multirange constructor shouldn't have arguments")));
+ elog(ERROR, /* can't happen */
+ "niladic multirange constructor must not receive arguments");
}
@@ -1031,7 +1044,9 @@ range_agg_transfn(PG_FUNCTION_ARGS)
rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
if (!type_is_range(rngtypoid))
- ereport(ERROR, (errmsg("range_agg must be called with a range")));
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_agg must be called with a range")));
if (PG_ARGISNULL(0))
state = initArrayResult(rngtypoid, aggContext, false);
@@ -1100,7 +1115,9 @@ multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
if (!type_is_multirange(mltrngtypoid))
- ereport(ERROR, (errmsg("range_intersect_agg must be called with a multirange")));
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_intersect_agg must be called with a multirange")));
typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
--
2.20.1
[text/x-diff] v12-0005-pgindent-stuff.patch (9.8K, ../../20200313213906.GA16075@alvherre.pgsql/6-v12-0005-pgindent-stuff.patch)
download | inline diff:
From 52ed3748aaedb8ae0689d32e07be21791f1f9e7a Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 9 Mar 2020 16:23:20 -0300
Subject: [PATCH v12 5/5] pgindent stuff
---
src/backend/utils/adt/multirangetypes.c | 32 ++++++-------
src/include/utils/multirangetypes.h | 64 ++++++++++++-------------
src/tools/pgindent/typedefs.list | 3 ++
3 files changed, 51 insertions(+), 48 deletions(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index e58ab5baf3..5542265819 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -576,7 +576,7 @@ multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
* short varlena header.
*/
void
-multirange_deserialize(MultirangeType * multirange,
+multirange_deserialize(MultirangeType *multirange,
int32 *range_count, RangeType ***ranges)
{
RangeType *r;
@@ -717,7 +717,7 @@ multirange_constructor1(PG_FUNCTION_ARGS)
Oid rngtypid;
TypeCacheEntry *typcache;
TypeCacheEntry *rangetyp;
- RangeType *range;
+ RangeType *range;
typcache = multirange_get_typcache(fcinfo, mltrngtypid);
rangetyp = typcache->rngtype;
@@ -836,8 +836,8 @@ multirange_minus(PG_FUNCTION_ARGS)
MultirangeType *
multirange_minus_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
- int32 range_count1, RangeType **ranges1,
- int32 range_count2, RangeType **ranges2)
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
{
RangeType *r1;
RangeType *r2;
@@ -1302,7 +1302,7 @@ elem_contained_by_multirange(PG_FUNCTION_ARGS)
* Test whether multirange mr contains a specific element value.
*/
bool
-multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr, Datum val)
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr, Datum val)
{
TypeCacheEntry *rangetyp;
int32 range_count;
@@ -1356,7 +1356,7 @@ range_contained_by_multirange(PG_FUNCTION_ARGS)
* Test whether multirange mr contains a specific range r.
*/
bool
-multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr, RangeType *r)
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr, RangeType *r)
{
TypeCacheEntry *rangetyp;
int32 range_count;
@@ -1390,7 +1390,7 @@ multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr
/* equality (internal version) */
bool
-multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
{
int32 range_count_1;
int32 range_count_2;
@@ -1437,7 +1437,7 @@ multirange_eq(PG_FUNCTION_ARGS)
/* inequality (internal version) */
bool
-multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
{
return (!multirange_eq_internal(typcache, mr1, mr2));
}
@@ -1493,7 +1493,7 @@ multirange_overlaps_multirange(PG_FUNCTION_ARGS)
}
bool
-range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType * mr)
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType *mr)
{
TypeCacheEntry *rangetyp;
int32 range_count;
@@ -1529,8 +1529,8 @@ range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, Multi
}
bool
-multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
- MultirangeType * mr2)
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
{
TypeCacheEntry *rangetyp;
int32 range_count1;
@@ -1744,7 +1744,7 @@ multirange_contained_by_multirange(PG_FUNCTION_ARGS)
*/
bool
multirange_contains_multirange_internal(TypeCacheEntry *typcache,
- MultirangeType * mr1, MultirangeType * mr2)
+ MultirangeType *mr1, MultirangeType *mr2)
{
TypeCacheEntry *rangetyp;
int32 range_count1;
@@ -1883,7 +1883,7 @@ multirange_after_multirange(PG_FUNCTION_ARGS)
/* strictly left of? (internal version) */
bool
range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
- MultirangeType * mr)
+ MultirangeType *mr)
{
int32 range_count;
RangeType **ranges;
@@ -1897,8 +1897,8 @@ range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
}
bool
-multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
- MultirangeType * mr2)
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
{
int32 range_count1;
int32 range_count2;
@@ -1918,7 +1918,7 @@ multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType *
/* strictly right of? (internal version) */
bool
range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
- MultirangeType * mr)
+ MultirangeType *mr)
{
int32 range_count;
RangeType **ranges;
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
index 98a8878ca0..469444776c 100644
--- a/src/include/utils/multirangetypes.h
+++ b/src/include/utils/multirangetypes.h
@@ -34,7 +34,7 @@ typedef struct
* because even their base types can be varlena. So we can't really index
* into this list.
*/
-} MultirangeType;
+} MultirangeType;
/* Use this macro in preference to fetching multirangetypid field directly */
#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
@@ -55,49 +55,49 @@ typedef struct
*/
/* internal versions of the above */
-extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
- MultirangeType * mr2);
-extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
- MultirangeType * mr2);
-extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr,
Datum elem);
-extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr,
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr,
RangeType *r);
extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
- MultirangeType * mr1,
- MultirangeType * mr2);
+ MultirangeType *mr1,
+ MultirangeType *mr2);
extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
- MultirangeType * mr);
+ MultirangeType *mr);
extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
- MultirangeType * mr1,
- MultirangeType * mr2);
+ MultirangeType *mr1,
+ MultirangeType *mr2);
extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
- MultirangeType * mr);
+ MultirangeType *mr);
extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
- MultirangeType * mr);
+ MultirangeType *mr);
extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
- MultirangeType * mr1,
- MultirangeType * mr2);
-extern MultirangeType * multirange_minus_internal(Oid mltrngtypoid,
- TypeCacheEntry *rangetyp,
- int32 range_count1,
- RangeType **ranges1,
- int32 range_count2,
- RangeType **ranges2);
-extern MultirangeType * multirange_intersect_internal(Oid mltrngtypoid,
- TypeCacheEntry *rangetyp,
- int32 range_count1,
- RangeType **ranges1,
- int32 range_count2,
- RangeType **ranges2);
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern MultirangeType *multirange_minus_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType *multirange_intersect_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
/* assorted support functions */
extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
Oid mltrngtypid);
-extern void multirange_deserialize(MultirangeType * range,
+extern void multirange_deserialize(MultirangeType *range,
int32 *range_count, RangeType ***ranges);
-extern MultirangeType * make_multirange(Oid mltrngtypoid,
- TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
-extern MultirangeType * make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+extern MultirangeType *make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType *make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
#endif /* MULTIRANGETYPES_H */
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index e216de9570..c23ce28af1 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1354,6 +1354,9 @@ MultiXactMember
MultiXactOffset
MultiXactStateData
MultiXactStatus
+MultirangeIOData
+MultirangeParseState
+MultirangeType
MyData
NDBOX
NODE
--
2.20.1
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 21:39 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-03-14 17:34 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-20 14:48 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2020-03-14 17:34 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
On Fri, Mar 13, 2020 at 2:39 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
> Here's the rebased version.
>
> I just realized I didn't include the API change I proposed in
> https://postgr.es/m/20200306200343.GA625@alvherre.pgsql ...
Thanks for your help with this Alvaro!
I was just adding your changes to my own branch and I noticed your
v12-0001 has different parameter names here:
diff --git a/src/backend/utils/adt/multirangetypes.c
b/src/backend/utils/adt/multirangetypes.c
index f9dd0378cc..0c9afd5448 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -376,11 +375,11 @@ multirange_typanalyze(PG_FUNCTION_ARGS)
* pointer to a type cache entry.
*/
static MultirangeIOData *
-get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid,
IOFuncSelector func)
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
IOFuncSelector func)
{
MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
- if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ if (cache == NULL || cache->typcache->type_id != rngtypid)
{
int16 typlen;
bool typbyval;
@@ -389,9 +388,9 @@ get_multirange_io_data(FunctionCallInfo fcinfo,
Oid mltrngtypid, IOFuncSelector
cache = (MultirangeIOData *)
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(MultirangeIOData));
- cache->typcache = lookup_type_cache(mltrngtypid,
TYPECACHE_MULTIRANGE_INFO);
+ cache->typcache = lookup_type_cache(rngtypid,
TYPECACHE_MULTIRANGE_INFO);
if (cache->typcache->rngtype == NULL)
- elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ elog(ERROR, "type %u is not a multirange type", rngtypid);
/* get_type_io_data does more than we need, but is convenient */
get_type_io_data(cache->typcache->rngtype->type_id,
I'm pretty sure mltrngtypid is the correct name here. Right? Let me
know if I'm missing something. :-)
Yours,
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 21:39 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-14 17:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-03-20 14:48 ` Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 0 replies; 152+ messages in thread
From: Alvaro Herrera @ 2020-03-20 14:48 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
On 2020-Mar-14, Paul A Jungwirth wrote:
> On Fri, Mar 13, 2020 at 2:39 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
> > Here's the rebased version.
> >
> > I just realized I didn't include the API change I proposed in
> > https://postgr.es/m/20200306200343.GA625@alvherre.pgsql ...
>
> Thanks for your help with this Alvaro!
>
> I was just adding your changes to my own branch and I noticed your
> v12-0001 has different parameter names here:
>
> static MultirangeIOData *
> -get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid,
> IOFuncSelector func)
> +get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
> IOFuncSelector func)
> I'm pretty sure mltrngtypid is the correct name here. Right? Let me
> know if I'm missing something. :-)
Heh. The intention here was to abbreviate to "typid", but if you want
to keep the longer name, it's OK too. I don't think that name is
particularly critical, since it should be obvious that it must be a
multirange type.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
@ 2020-03-14 18:13 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-17 04:52 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
1 sibling, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2020-03-14 18:13 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
On Fri, Mar 13, 2020 at 10:06 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
>
> Paul A Jungwirth <pj@illuminatedcomputing.com> writes:
> > On Wed, Mar 11, 2020 at 4:39 PM Paul A Jungwirth
> > <pj@illuminatedcomputing.com> wrote:
> >> Oh, my last email left out the most important part. :-) Is this
> >> failure online somewhere so I can take a look at it and fix it?
>
> Look for your patch(es) at
>
> http://commitfest.cputube.org
>
> Right now it's not even applying, presumably because Alvaro already
> pushed some pieces, so you need to rebase. But when it was applying,
> one or both of the test builds was failing.
Here are all Alvaro's changes rolled into one patch, along with the
get_multirange_io_data parameter renamed to mltrngtypid, and this
small fix to the dependency regression test:
diff --git a/src/test/regress/expected/dependency.out
b/src/test/regress/expected/dependency.out
index 778699a961..8232795148 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -140,8 +140,8 @@ owner of sequence deptest_a_seq
owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
-owner of type deptest_range
owner of type deptest_multirange
+owner of type deptest_range
owner of table deptest2
owner of sequence ss1
owner of type deptest_t
I think that should fix the cfbot failure.
Yours,
Paul
Attachments:
[application/octet-stream] v13-multiranges.patch (326.4K, ../../CA+renyXBduODHi34ASUZrQA=Yza-q6WXdrMa102soxAfw-Be0A@mail.gmail.com/2-v13-multiranges.patch)
download | inline diff:
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 9ec1af780b..92a1a254ac 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -229,9 +229,9 @@
</indexterm>
<para>
- Five pseudo-types of special interest are <type>anyelement</type>,
+ Six pseudo-types of special interest are <type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
- and <type>anyrange</type>,
+ <type>anyrange</type>, and <type>anymultirange</type>.
which are collectively called <firstterm>polymorphic types</firstterm>.
Any function declared using these types is said to be
a <firstterm>polymorphic function</firstterm>. A polymorphic function can
@@ -250,15 +250,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type>, the actual range type in
- the <type>anyrange</type> positions must be a range whose subtype is
- the same type appearing in the <type>anyelement</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -268,6 +268,20 @@
</para>
<para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type>, the actual range type in
+ the <type>anyrange</type> positions must be a range whose subtype is
+ the same type appearing in the <type>anyelement</type> positions.
+ The type <type>anymultirange</type> accepts a multirange
+ whose contents match the other polymorphic types.
+ That is it must hold ranges matching <type>anyrange</type>
+ and base type elements matching <type>anyelement</type> (if either of
+ those are present). If <type>anyarray</type> is present its elements
+ must match the base type of the <type>anyrange</type> and/or the base
+ type of the ranges in an <type>anymultirange</type>.
+ </para>
+
+ <para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
types are allowed. For example, a function declared as
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 323366feb6..cfe2c23e65 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -13546,7 +13546,7 @@ SELECT NULLIF(value, '(none)') ...
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14017,12 +14017,14 @@ NULL baz</literallayout>(3 rows)</entry>
<title>Range Functions and Operators</title>
<para>
- See <xref linkend="rangetypes"/> for an overview of range types.
+ See <xref linkend="rangetypes"/> for an overview of range and multirange types.
</para>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14040,134 +14042,215 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
- <entry><literal>f</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">f</literallayout></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
- <entry><literal>[5,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)
+'{[5,10)}'::nummultirange + '{[15,20)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,20)
+{[5,10), [15,20)}</literallayout></entry>
</row>
<row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
- <entry><literal>[10,15)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)
+'{[5,15)}'::int8multirange * '{[10,20)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[10,15)
+{[10,15)}</literallayout></entry>
</row>
<row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
- <entry><literal>[5,10)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)
+'{[5,20)}'::int8multirange - '{[10,15)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,10)
+{[5,10), [15,20)}</literallayout></entry>
</row>
</tbody>
@@ -14185,19 +14268,31 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. There are separate operators for union and difference that take
+ multirange parameters and return a multirange, and they do not fail even if
+ their arguments are disjoint. So if you need a union or difference operation
+ for ranges that may be disjoint, you can avoid errors by first casting your
+ ranges to multiranges.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14249,6 +14344,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14260,6 +14366,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>isempty</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14271,6 +14388,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14282,6 +14410,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14293,6 +14432,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14304,6 +14454,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14315,6 +14476,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>range_merge</function>(<type>anyrange</type>, <type>anyrange</type>)
</literal>
</entry>
@@ -14323,16 +14495,38 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
+ <function>multirange</function>(<type>anyrange</type>)
+ </literal>
+ </entry>
+ <entry><type>anymultirange</type></entry>
+ <entry>a multirange containing just the given range</entry>
+ <entry><literal>multirange('[1,2)'::int4range)</literal></entry>
+ <entry><literal>{[1,2)}</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -14654,6 +14848,44 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>string_agg</primary>
</indexterm>
<function>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index b75fb3a392..c8784bdce3 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -232,10 +239,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -269,6 +296,19 @@ SELECT int8range(1, 14, '(]');
SELECT numrange(NULL, 2.2);
</programlisting>
</para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
+</programlisting>
+ </para>
</sect2>
<sect2 id="rangetypes-discrete">
@@ -342,6 +382,11 @@ SELECT '[1.234, 5.678]'::floatrange;
</para>
<para>
+ When you define your own range you automatically get a corresponding
+ multirange type.
+ </para>
+
+ <para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
ordering that determines which values fall into a given range.
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 423fd79d94..fb375cd3bf 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -192,6 +192,7 @@ ProcedureCreate(const char *procedureName,
genericInParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericInParam = true;
anyrangeInParam = true;
break;
@@ -219,6 +220,7 @@ ProcedureCreate(const char *procedureName,
genericOutParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericOutParam = true;
anyrangeOutParam = true;
break;
@@ -231,10 +233,10 @@ ProcedureCreate(const char *procedureName,
/*
* Do not allow polymorphic return type unless at least one input argument
- * is polymorphic. ANYRANGE return type is even stricter: must have an
- * ANYRANGE input (since we can't deduce the specific range type from
- * ANYELEMENT). Also, do not allow return type INTERNAL unless at least
- * one input argument is INTERNAL.
+ * is polymorphic. ANYRANGE and ANYMULTIRANGE return types are even
+ * stricter: must have an ANYRANGE or ANYMULTIRANGE input (since we can't
+ * deduce the specific range type from ANYELEMENT). Also, do not allow
+ * return type INTERNAL unless at least one input argument is INTERNAL.
*/
if ((IsPolymorphicType(returnType) || genericOutParam)
&& !genericInParam)
@@ -243,12 +245,13 @@ ProcedureCreate(const char *procedureName,
errmsg("cannot determine result data type"),
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
- if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
- !anyrangeInParam)
+ if ((returnType == ANYRANGEOID ||
+ returnType == ANYMULTIRANGEOID ||
+ anyrangeOutParam) && !anyrangeInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
- errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
+ errdetail("A function returning \"anyrange\" or \"anymultirange\" must have at least one \"anyrange\" or \"anymultirange\" argument.")));
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
ereport(ERROR,
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index b5bc36c2bd..1217a6ddd0 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,12 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index cd56714968..c359f27c74 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -27,6 +27,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
@@ -36,6 +37,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
+ bool tryOriginal);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -809,31 +813,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
char *
makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
- char *arr = (char *) palloc(NAMEDATALEN);
- int namelen = strlen(typeName);
- int i;
-
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
+ char *arr;
- if (i >= NAMEDATALEN - 1)
+ arr = makeUniqueTypeName(typeName, typeNamespace, false);
+ if (arr == NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -904,3 +887,91 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char *buf;
+ char *mrname;
+ char *rangestr;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "_multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ char *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
+
+ buf = psprintf("%s%s%s", prefix, "multi", rangestr);
+ }
+ else
+ buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
+
+ /* clip it at NAMEDATALEN-1 bytes */
+ buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
+
+ mrname = makeUniqueTypeName(buf, typeNamespace, true);
+ if (mrname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mrname;
+}
+
+/*
+ * makeUniqueTypeName
+ * Generate a unique name for a prospective new type
+ *
+ * Given a typeName, return a new palloc'ed name by preprending underscores
+ * until a non-conflicting name results.
+ *
+ * If tryOriginal, first try with zero underscores.
+ */
+static char *
+makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
+{
+ int i;
+ int namelen;
+ char dest[NAMEDATALEN];
+
+ Assert(strlen(typeName) <= NAMEDATALEN - 1);
+
+ if (tryOriginal &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(typeName);
+
+ /*
+ * The idea is to prepend underscores as needed until we make a name that
+ * doesn't collide with anything ...
+ */
+ namelen = strlen(typeName);
+ for (i = 1; i < NAMEDATALEN - 1; i++)
+ {
+ dest[i - 1] = '_';
+ strlcpy(dest + i, typeName, NAMEDATALEN - i);
+ if (namelen + i >= NAMEDATALEN)
+ truncate_identifier(dest, NAMEDATALEN, false);
+
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(dest);
+ }
+
+ return NULL;
+}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 8891b1d564..dfb2edd3d0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -105,9 +105,14 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid,
+ Oid rangeArrayOid, Oid *castFuncOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -738,7 +743,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1279,6 +1285,11 @@ checkEnumOwner(HeapTuple tup)
/*
* DefineRange
* Registers a new range type.
+ *
+ * Perhaps it might be worthwhile to set pg_type.typelem to the base type,
+ * and likewise on multiranges to set it to the range type. But having a
+ * non-zero typelem is treated elsewhere as a synonym for being an array,
+ * and users might have queries with that same assumption.
*/
ObjectAddress
DefineRange(CreateRangeStmt *stmt)
@@ -1287,7 +1298,11 @@ DefineRange(CreateRangeStmt *stmt)
Oid typeNamespace;
Oid typoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1304,6 +1319,8 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
+ Oid castFuncOid;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1452,8 +1469,10 @@ DefineRange(CreateRangeStmt *stmt)
/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for ranges */
alignment = (subtypalign == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
- /* Allocate OID for array type */
+ /* Allocate OID for array type, its multirange, and its multirange array */
rangeArrayOid = AssignTypeArrayOid();
+ multirangeOid = AssignTypeMultirangeOid();
+ multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
address =
@@ -1491,9 +1510,46 @@ DefineRange(CreateRangeStmt *stmt)
Assert(typoid == InvalidOid || typoid == address.objectId);
typoid = address.objectId;
+ /* Create the multirange that goes with it */
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(multirangeOid, /* force assignment of this type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_RANGE, /* type-category (range type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ Assert(multirangeOid == mltrngaddress.objectId);
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
@@ -1534,8 +1590,53 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ multirangeOid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ multirangeOid, typoid, rangeArrayOid,
+ &castFuncOid);
+
+ /* Create cast from the range type to its multirange type */
+ CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1613,6 +1714,147 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ *
+ * Sets castFuncOid to the oid of the new constructor that can be used
+ * to cast from a range to a multirange.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
+ Oid *castFuncOid)
+{
+ ObjectAddress myself,
+ referenced;
+ oidvector *argtypes;
+ Datum allParamTypes;
+ ArrayType *allParameterTypes;
+ Datum paramModes;
+ ArrayType *parameterModes;
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ /* 0-arg constructor - for empty multiranges */
+ argtypes = buildoidvector(NULL, 0);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor0", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /*
+ * Make the constructor internally-dependent on the multirange type so
+ * that they go away silently when the type is dropped. Note that pg_dump
+ * depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+
+ /*
+ * 1-arg constructor - for casts
+ *
+ * In theory we shouldn't need both this and the vararg (n-arg) constructor,
+ * but having a separate 1-arg function lets us define casts against it.
+ */
+ argtypes = buildoidvector(&rangeOid, 1);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor1", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ true, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ *castFuncOid = myself.objectId;
+
+ /* n-arg constructor - vararg */
+ argtypes = buildoidvector(&rangeArrayOid, 1);
+ allParamTypes = ObjectIdGetDatum(rangeArrayOid);
+ allParameterTypes = construct_array(&allParamTypes,
+ 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
+ parameterModes = construct_array(¶mModes, 1, CHAROID,
+ 1, true, 'c');
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor2", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(allParameterTypes), /* allParameterTypes */
+ PointerGetDatum(parameterModes), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ pfree(allParameterTypes);
+ pfree(parameterModes);
+}
/*
* Find suitable I/O functions for a type.
@@ -2045,6 +2287,72 @@ AssignTypeArrayOid(void)
return type_array_oid;
}
+/*
+ * AssignTypeMultirangeOid
+ *
+ * Pre-assign the range type's multirange OID for use in pg_type.oid
+ */
+Oid
+AssignTypeMultirangeOid(void)
+{
+ Oid type_multirange_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
+
+ type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
+ binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_oid;
+}
+
+/*
+ * AssignTypeMultirangeArrayOid
+ *
+ * Pre-assign the range type's multirange array OID for use in pg_type.typarray
+ */
+Oid
+AssignTypeMultirangeArrayOid(void)
+{
+ Oid type_multirange_array_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
+
+ type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
+ binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_array_oid;
+}
+
/*-------------------------------------------------------------------
* DefineCompositeType
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 929f758ef4..27bfc4bf8c 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -187,18 +187,20 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
- targetTypeId == ANYRANGEOID)
+ targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call anyarray_in,
- * anyenum_in, or anyrange_in, which will produce an error. Also, if
- * what we have is a domain over array, enum, or range, we have to
- * relabel it to its base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * anyarray_in, anyenum_in, anyrange_in, or anymultirange_in, which
+ * will produce an error. Also, if what we have is a domain over
+ * array, enum, range, or multirange, we have to relabel it to its
+ * base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1481,6 +1483,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid array_typelem;
Oid range_typeid = InvalidOid;
Oid range_typelem;
+ Oid multirange_typeid = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
@@ -1527,6 +1531,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1577,6 +1590,41 @@ check_generic_type_consistency(const Oid *actual_arg_types,
/* otherwise, they better match */
return false;
}
+ else
+ range_typelem = InvalidOid; /* keep compiler quiet */
+ }
+ else
+ range_typelem = InvalidOid; /* keep compiler quiet */
+
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_range_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
}
if (have_anynonarray)
@@ -1680,13 +1728,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid array_typelem;
- Oid range_typelem;
+ Oid range_typelem = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
+ bool have_anyrange = (rettype == ANYRANGEOID);
+ bool have_anymultirange = (rettype == ANYMULTIRANGEOID);
/*
* Loop through the arguments to see if we have any that are polymorphic.
@@ -1744,7 +1796,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else if (decl_type == ANYRANGEOID)
{
- have_generics = true;
+ have_generics = have_anyrange = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
@@ -1762,6 +1814,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ have_generics = have_anymultirange = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
}
/*
@@ -1812,9 +1884,12 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- if (range_typeid == ANYRANGEOID && !have_anyelement)
+ if (range_typeid == ANYRANGEOID && !have_anyelement && !have_anymultirange)
{
- /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
+ /*
+ * Special case for ANYRANGE input: okay iff no ANYELEMENT or
+ * ANYMULTIRANGE
+ */
range_typelem = ANYELEMENTOID;
}
else
@@ -1846,6 +1921,71 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(range_typeid),
format_type_be(elem_typeid))));
}
+ else
+ range_typelem = InvalidOid;
+ }
+
+ /* Get the range type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ if (multirange_typeid == ANYMULTIRANGEOID && !have_anyrange && !have_anyelement)
+ {
+ /*
+ * Special case for ANYMULTIRANGE input: okay iff no ANYRANGE or
+ * ANYELEMENT
+ */
+ multirange_typelem = ANYRANGEOID;
+ }
+ else
+ {
+ multirange_typelem = get_range_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+ }
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ /*
+ * Infer the element type too if needed (if there is an ANYMULTIRANGE
+ * and ANYELEMENT, with no ANYRANGE).
+ */
+ if (!OidIsValid(elem_typeid))
+ {
+ elem_typeid = get_range_subtype(range_typeid);
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
}
if (!OidIsValid(elem_typeid))
@@ -1855,6 +1995,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -1927,6 +2068,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -1958,6 +2110,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
@@ -2006,8 +2174,7 @@ resolve_generic_type(Oid declared_type,
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
- context_declared_type == ANYENUMOID ||
- context_declared_type == ANYRANGEOID)
+ context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
@@ -2019,11 +2186,37 @@ resolve_generic_type(Oid declared_type,
format_type_be(context_actual_type))));
return array_typeid;
}
+ else if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid range_typelem = get_range_subtype(context_base_type);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_range_multirange_subtype(context_base_type);
+ Oid range_typelem = get_range_subtype(multirange_typelem);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
- declared_type == ANYENUMOID ||
- declared_type == ANYRANGEOID)
+ declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
@@ -2051,6 +2244,19 @@ resolve_generic_type(Oid declared_type,
"anyrange", format_type_be(context_base_type))));
return range_typelem;
}
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ /* Use the element type corresponding to actual type */
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_range_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
@@ -2059,6 +2265,74 @@ resolve_generic_type(Oid declared_type,
return context_actual_type;
}
}
+ else if (declared_type == ANYRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_range_multirange_subtype(context_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return multirange_typelem;
+ }
+ else
+ {
+ /* We can't infer a range from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
+ else if (declared_type == ANYMULTIRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typeid = get_range_multirange(context_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return multirange_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else
+ {
+ /* We can't infer a multirange from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
else
{
/* declared_type isn't polymorphic, so return it as-is */
@@ -2173,6 +2447,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..7118dd0034 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..bd3cf3dc93
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,2186 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ FmgrInfo typioproc; /* range type's I/O proc */
+ Oid typioparam; /* range type's I/O parameter */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData *get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks either either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str = NULL;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */ ;
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **)
+ repalloc(ranges, range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(InputFunctionCall(&cache->typioproc,
+ range_str_copy,
+ cache->typioparam,
+ typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->typioproc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(ReceiveFunctionCall(&cache->typioproc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range;
+
+ range = RangeTypePGetDatum(ranges[i]);
+ range = PointerGetDatum(SendFunctionCall(&cache->typioproc, range));
+
+ pq_sendint32(buf, VARSIZE(range) - VARHDRSZ);
+ pq_sendbytes(buf, VARDATA(range), VARSIZE(range) - VARHDRSZ);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ Oid typiofunc;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &typiofunc);
+
+ if (!OidIsValid(typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(typiofunc, &cache->typioproc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ bytelen += MAXALIGN(VARSIZE(ranges[i]));
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ memcpy(ptr, ranges[i], VARSIZE(ranges[i]));
+ ptr += MAXALIGN(VARSIZE(ranges[i]));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of arbitrary ranges into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ *
+ * Returns the number of slots actually used, which may be less than
+ * input_range_count but never more.
+ *
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType *multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor2(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_CARDINALITY_VIOLATION),
+ errmsg("multiranges cannot be constructed from multi-dimensional arrays")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Construct multirange value from a single range.
+ * It'd be nice if we could just use multirange_constructor2
+ * for this case, but we need a non-variadic single-arg function
+ * to let us define a CAST from a range to its multirange.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ RangeType *range;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ range = PG_GETARG_RANGE_P(0);
+
+ /* Make sure the range type matches. */
+ rngtypid = RangeTypeGetOid(range);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 1, &range));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ elog(ERROR, /* can't happen */
+ "niladic multirange constructor must not receive arguments");
+}
+
+
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+multirange_union(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
+}
+
+/* multirange minus */
+Datum
+multirange_minus(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+multirange_intersect(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+ /*-----------------------------------------------
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1,
+ * but one extra won't hurt.
+ *-----------------------------------------------
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType *mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges1[range_count1 - 1],
+ ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype,
+ ranges1[0],
+ ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1, MultirangeType *mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
+ return false;
+
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
+ return false;
+ }
+
+ /* All ranges in mr2 are satisfied */
+ return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 18f2ee8226..248ea7f44a 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -52,6 +52,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
}
Datum
+binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
+binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_array_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
binary_upgrade_set_next_toast_pg_type_oid(PG_FUNCTION_ARGS)
{
Oid typoid = PG_GETARG_OID(0);
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 4653fc33e6..c909c9dc8c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -185,6 +186,30 @@ anyrange_out(PG_FUNCTION_ARGS)
}
/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
+/*
* void_in - input routine for pseudo-type VOID.
*
* We allow this so that PL functions can return VOID without any special
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 01ad8bc240..0af3c67296 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -431,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -452,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -475,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -485,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -495,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -505,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -515,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -957,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -969,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -993,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1101,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1110,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1132,9 +1180,81 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
+}
+
+/* range, range -> range, range functions */
+
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
+ {
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
+ }
+
+ return false;
}
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
+}
+
+
/* Btree support */
/* btree comparator */
@@ -1144,12 +1264,6 @@ range_cmp(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
- RangeBound lower1,
- lower2;
- RangeBound upper1,
- upper2;
- bool empty1,
- empty2;
int cmp;
check_stack_depth(); /* recurses when subtype is a range type */
@@ -1160,6 +1274,28 @@ range_cmp(PG_FUNCTION_ARGS)
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+ cmp = range_cmp_internal(typcache, r1, r2);
+
+ PG_FREE_IF_COPY(r1, 0);
+ PG_FREE_IF_COPY(r2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
@@ -1177,10 +1313,7 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
- PG_FREE_IF_COPY(r1, 0);
- PG_FREE_IF_COPY(r2, 1);
-
- PG_RETURN_INT32(cmp);
+ return cmp;
}
/* inequality operators using the range_cmp function */
@@ -1218,13 +1351,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1363,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1404,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1433,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1471,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1770,6 +1917,21 @@ range_get_flags(const RangeType *range)
}
/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
+/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
* This is only needed in GiST operations, so we don't include a provision
@@ -1938,6 +2100,46 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
}
/*
+ * qsort callback for sorting ranges.
+ *
+ * Two empty ranges compare equal; an empty range sorts to the left of any
+ * non-empty range. Two non-empty ranges are sorted by lower bound first
+ * and by upper bound next.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
+/*
* Build an empty range value of the type indicated by the typcache entry.
*/
RangeType *
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 27bbb58f56..c6c079c623 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2495,6 +2495,16 @@ type_is_range(Oid typid)
}
/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
+/*
* get_type_category_preferred
*
* Given the type OID, fetch its category and preferred-type status.
@@ -3149,7 +3159,7 @@ get_namespace_name_or_temp(Oid nspid)
return get_namespace_name(nspid);
}
-/* ---------- PG_RANGE CACHE ---------- */
+/* ---------- PG_RANGE CACHES ---------- */
/*
* get_range_subtype
@@ -3202,6 +3212,56 @@ get_range_collation(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngmultitypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_range_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_range_multirange_subtype(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGEMULTIRANGE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 53d9ddf159..7654331a59 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -651,6 +651,18 @@ static const struct cachedesc cacheinfo[] = {
},
64
},
+ {RangeRelationId, /* RANGEMULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngmultitypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+
{RangeRelationId, /* RANGETYPE */
RangeTypidIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 854f133f9b..160846cf10 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -286,6 +286,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -302,6 +303,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -553,8 +557,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -591,7 +595,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -616,7 +620,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -641,7 +645,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -693,6 +697,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -737,6 +748,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -817,6 +835,16 @@ lookup_type_cache(Oid type_id, int flags)
}
/*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
+ /*
* If requested, get information about a domain type
*/
if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
@@ -927,6 +955,22 @@ load_rangetype_info(TypeCacheEntry *typentry)
typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
}
+/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Oid rangetypeOid;
+
+ rangetypeOid = get_range_multirange_subtype(typentry->type_id);
+ if (!OidIsValid(rangetypeOid))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
/*
* load_domaintype_info --- helper routine to set up domain constraint info
@@ -1527,11 +1571,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1574,6 +1618,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 0201e4f8d3..b10cd99144 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -470,11 +470,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anynonarray = false;
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
Oid anycollation = InvalidOid;
int i;
@@ -500,12 +502,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
case ANYRANGEOID:
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_anymultirange_result = true;
+ break;
default:
break;
}
}
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/*
@@ -533,6 +538,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (!OidIsValid(anyrange_type))
anyrange_type = get_call_expr_argtype(call_expr, i);
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(anymultirange_type))
+ anymultirange_type = get_call_expr_argtype(call_expr, i);
+ break;
default:
break;
}
@@ -540,7 +549,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/* If nothing found, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -561,19 +570,131 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ rngtype = get_range_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ anymultirange_type = mltrngtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
/* Enforce ANYNONARRAY if needed */
if (have_anynonarray && type_is_array(anyelement_type))
@@ -640,6 +761,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
default:
break;
}
@@ -664,9 +793,11 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
int inargno;
int i;
@@ -725,6 +856,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ have_anymultirange_result = true;
+ else
+ {
+ if (!OidIsValid(anymultirange_type))
+ {
+ anymultirange_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anymultirange_type))
+ return false;
+ }
+ argtypes[i] = anymultirange_type;
+ }
+ break;
default:
break;
}
@@ -734,12 +880,12 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
/* Done? */
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/* If no input polymorphics, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -760,19 +906,132 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ rngtype = get_range_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ }
+ else
+ {
+ return false;
+ }
+ }
/* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */
@@ -792,6 +1051,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = anymultirange_type;
+ break;
default:
break;
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 28312e14ef..ba53ac13ba 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -271,7 +271,8 @@ static void dumpSearchPath(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type);
+ bool force_array_type,
+ bool include_multirange_type);
static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
static void binary_upgrade_set_pg_class_oids(Archive *fout,
@@ -1580,7 +1581,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4339,16 +4340,49 @@ append_depends_on_extension(Archive *fout,
}
}
+static Oid
+get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+{
+ /*
+ * If the old version didn't assign an array type, but the new version
+ * does, we must select an unused type OID to assign. This currently only
+ * happens for domains, when upgrading pre-v11 to v11 and up.
+ *
+ * Note: local state here is kind of ugly, but we must have some, since we
+ * mustn't choose the same unused OID more than once.
+ */
+ static Oid next_possible_free_oid = FirstNormalObjectId;
+ PGresult *res;
+ bool is_dup;
+
+ do
+ {
+ ++next_possible_free_oid;
+ printfPQExpBuffer(upgrade_query,
+ "SELECT EXISTS(SELECT 1 "
+ "FROM pg_catalog.pg_type "
+ "WHERE oid = '%u'::pg_catalog.oid);",
+ next_possible_free_oid);
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+ is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
+ PQclear(res);
+ } while (is_dup);
+
+ return next_possible_free_oid;
+}
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type)
+ bool force_array_type,
+ bool include_multirange_type)
{
PQExpBuffer upgrade_query = createPQExpBuffer();
PGresult *res;
Oid pg_type_array_oid;
+ Oid pg_type_multirange_oid;
+ Oid pg_type_multirange_array_oid;
appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
@@ -4369,33 +4403,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQclear(res);
if (!OidIsValid(pg_type_array_oid) && force_array_type)
- {
- /*
- * If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
- *
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
- */
- static Oid next_possible_free_oid = FirstNormalObjectId;
- bool is_dup;
-
- do
- {
- ++next_possible_free_oid;
- printfPQExpBuffer(upgrade_query,
- "SELECT EXISTS(SELECT 1 "
- "FROM pg_catalog.pg_type "
- "WHERE oid = '%u'::pg_catalog.oid);",
- next_possible_free_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
- is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
- PQclear(res);
- } while (is_dup);
-
- pg_type_array_oid = next_possible_free_oid;
- }
+ pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
if (OidIsValid(pg_type_array_oid))
{
@@ -4406,6 +4414,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
pg_type_array_oid);
}
+ /*
+ * Pre-set the multirange type oid and its own array type oid.
+ */
+ if (include_multirange_type)
+ {
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBuffer(upgrade_query,
+ "SELECT t.oid, t.typarray "
+ "FROM pg_catalog.pg_type t "
+ "JOIN pg_catalog.pg_range r "
+ "ON t.oid = r.rngmultitypid "
+ "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
+ pg_type_oid);
+
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+
+ pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+ pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
+
+ PQclear(res);
+ }
+ else
+ {
+ pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ }
+
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_oid);
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_array_oid);
+ }
+
destroyPQExpBuffer(upgrade_query);
}
@@ -4439,7 +4487,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
- pg_type_oid, false);
+ pg_type_oid, false, false);
if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
{
@@ -4988,6 +5036,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
@@ -8102,9 +8155,12 @@ getProcLangs(Archive *fout, int *numProcLangs)
/*
* getCasts
- * get basic information about every cast in the system
+ * get basic information about most casts in the system
*
* numCasts is set to the number of casts read in
+ *
+ * Skip casts from a range to its multirange, since we'll create those
+ * automatically.
*/
CastInfo *
getCasts(Archive *fout, int *numCasts)
@@ -8122,7 +8178,20 @@ getCasts(Archive *fout, int *numCasts)
int i_castcontext;
int i_castmethod;
- if (fout->remoteVersion >= 80400)
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBufferStr(query, "SELECT tableoid, oid, "
+ "castsource, casttarget, castfunc, castcontext, "
+ "castmethod "
+ "FROM pg_cast c "
+ "WHERE NOT EXISTS ( "
+ "SELECT 1 FROM pg_range r "
+ "WHERE c.castsource = r.rngtypid "
+ "AND c.casttarget = r.rngmultitypid "
+ ") "
+ "ORDER BY 3,4");
+ }
+ else if (fout->remoteVersion >= 80400)
{
appendPQExpBufferStr(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
@@ -10310,7 +10379,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
qualtypname);
@@ -10436,7 +10505,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, true);
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
qualtypname);
@@ -10542,7 +10611,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
qualtypname);
@@ -10747,7 +10816,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
@@ -10934,7 +11003,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- true); /* force array type */
+ true, /* force array type */
+ false); /* force multirange type */
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -11122,7 +11192,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
{
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
}
@@ -11396,7 +11466,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
stinfo->baseType->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
fmtQualifiedDumpable(stinfo));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index e0c6444ef6..90a93b4cb2 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -176,6 +176,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 70157cf90a..c262265b95 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -139,8 +139,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 12d94fe1b3..01d95117cd 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,6 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 8be303870f..566f9364c5 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -327,6 +327,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_rngmultitypid_index, 8001, on pg_range using btree(rngmultitypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index ffabe275c0..03bab74253 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..827ac4f03c 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 75c0152b66..cf0cac5641 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -285,6 +285,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
@@ -445,6 +447,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
@@ -1263,12 +1270,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index 6ef8b8a4e7..14277c8fdf 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -512,4 +512,17 @@
{ castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
castcontext => 'e', castmethod => 'f' },
+# range to multirange
+{ castsource => 'int4range', casttarget => 'int4multirange', castfunc => 'int4multirange(int4range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'int8range', casttarget => 'int8multirange', castfunc => 'int8multirange(int8range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'numrange', casttarget => 'nummultirange', castfunc => 'nummultirange(numrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'daterange', casttarget => 'datemultirange', castfunc => 'datemultirange(daterange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tsrange', casttarget => 'tsmultirange', castfunc => 'tsmultirange(tsrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tstzrange', casttarget => 'tstzmultirange', castfunc => 'tstzmultirange(tstzrange)',
+ castcontext => 'e', castmethod => 'f' },
]
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index ab2f50c9eb..eebebbcdb4 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 7c135da3b1..24f4a32203 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3297,5 +3297,174 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'contsel', oprjoin => 'contjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 26227df216..901fd7b303 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 7fb574f9dc..27f25d989b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9643,6 +9643,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9692,6 +9696,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9760,6 +9771,258 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8114',
+ proname => 'multirange_union', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union' },
+{ oid => '8120',
+ proname => 'multirange_minus', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus' },
+{ oid => '8126',
+ proname => 'multirange_intersect', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8146', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 't',
+ prorettype => 'int4multirange', proargtypes => 'int4range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8147', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 't',
+ prorettype => 'nummultirange', proargtypes => 'numrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8148', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 't',
+ prorettype => 'tsmultirange', proargtypes => 'tsrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8149', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 't',
+ prorettype => 'tstzmultirange', proargtypes => 'tstzrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8150', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 't',
+ prorettype => 'datemultirange', proargtypes => 'daterange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8151', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 't',
+ prorettype => 'int8multirange', proargtypes => 'int8range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8152', descr => 'anymultirange cast',
+ proname => 'multirange', proisstrict => 't',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
@@ -10142,6 +10405,14 @@
proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
+{ oid => '8144', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
+{ oid => '8145', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
{ oid => '3585', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index 479754c245..10060255c9 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ rngmultitypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', rngmultitypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', rngmultitypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', rngmultitypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ rngmultitypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ rngmultitypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index 98172bb1b6..60a8fde518 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -34,6 +34,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* OID of range's element type (subtype) */
Oid rngsubtype BKI_LOOKUP(pg_type);
+ /* OID of the range's multirange type */
+ Oid rngmultitypid BKI_LOOKUP(pg_type);
+
/* collation for this range type, or 0 */
Oid rngcollation BKI_DEFAULT(0);
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index b00597d6ff..6e1cb796f1 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -486,6 +486,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -590,5 +624,10 @@
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 97890946c5..d18f19f55a 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -263,6 +263,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -299,7 +300,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#endif /* EXPOSE_TO_CLIENT_CODE */
@@ -358,4 +360,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 0162bc2ffe..80e3a5e399 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
+extern Oid AssignTypeMultirangeOid(void);
+extern Oid AssignTypeMultirangeArrayOid(void);
extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 4e646c55e9..80410dcd04 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -155,6 +155,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -181,6 +182,8 @@ extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
extern Oid get_range_collation(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_range_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
extern bool get_index_isreplident(Oid index_oid);
extern bool get_index_isvalid(Oid index_oid);
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..469444776c
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern MultirangeType *multirange_minus_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType *multirange_intersect_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType *range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType *make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType *make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 0cbbf09033..204aee4054 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
@@ -92,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -115,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -125,6 +141,7 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
@@ -132,8 +149,12 @@ extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f27b73d76d..e8f393520a 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -79,6 +79,7 @@ enum SysCacheIdentifier
PUBLICATIONOID,
PUBLICATIONREL,
PUBLICATIONRELMAP,
+ RANGEMULTIRANGE,
RANGETYPE,
RELNAMENSP,
RELOID,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index cdd20e56d7..33f332a141 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -101,6 +101,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
/*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype; /* multirange's range underlying type */
+
+ /*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
*/
@@ -143,6 +148,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index c8e43e684f..2d59021d68 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -511,6 +511,8 @@ do_compile(FunctionCallInfo fcinfo,
rettypeid = INT4ARRAYOID;
else if (rettypeid == ANYRANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2087,6 +2089,7 @@ build_datatype(HeapTuple typeTup, int32 typmod,
case TYPTYPE_BASE:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
+ case TYPTYPE_MULTIRANGE:
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_COMPOSITE:
@@ -2501,6 +2504,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYRANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..8232795148 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -140,6 +140,7 @@ owner of sequence deptest_a_seq
owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
+owner of type deptest_multirange
owner of type deptest_range
owner of table deptest2
owner of sequence ss1
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..723f2408b0
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,2395 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+ int4multirange
+----------------
+ {}
+(1 row)
+
+select int4range(1, 3)::int4multirange;
+ int4range
+-----------
+ {[1,3)}
+(1 row)
+
+select int4range(1, null)::int4multirange;
+ int4range
+-----------
+ {[1,)}
+(1 row)
+
+select int4range(null, null)::int4multirange;
+ int4range
+-----------
+ {(,)}
+(1 row)
+
+select 'empty'::textrange::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textrange('a', 'c')::textmultirange;
+ textrange
+-----------
+ {[a,c)}
+(1 row)
+
+select textrange('a', null)::textmultirange;
+ textrange
+-----------
+ {[a,)}
+(1 row)
+
+select textrange(null, null)::textmultirange;
+ textrange
+-----------
+ {(,)}
+(1 row)
+
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT nummultirange() + nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT nummultirange() - nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() * nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+ mr_polymorphic
+----------------
+ {[1,4)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 40468e8f49..1514f3af7c 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -206,6 +213,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -224,6 +233,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -329,13 +340,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
@@ -343,16 +355,19 @@ ORDER BY 2;
2502 | anyarray_recv
2312 | anyelement_in
3504 | anyenum_in
+ 8002 | anymultirange_in
2777 | anynonarray_in
3832 | anyrange_in
750 | array_in
2400 | array_recv
3506 | enum_in
3532 | enum_recv
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(13 rows)
+(16 rows)
-- Look for functions that accept cstring and are neither datatype input
-- functions nor encoding conversion functions. It's almost never a good
@@ -2139,13 +2154,14 @@ ORDER BY 1, 2, 3;
| float_ops | float4_ops | real
| float_ops | float8_ops | double precision
| jsonb_ops | jsonb_ops | jsonb
+ | multirange_ops | multirange_ops | anymultirange
| numeric_ops | numeric_ops | numeric
| range_ops | range_ops | anyrange
| record_image_ops | record_image_ops | record
| record_ops | record_ops | record
| tsquery_ops | tsquery_ops | tsquery
| tsvector_ops | tsvector_ops | tsvector
-(14 rows)
+(15 rows)
-- **************** pg_index ****************
-- Look for illegal values in pg_index fields.
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 220f2d96cb..6d00a3efab 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1371,7 +1389,7 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
@@ -1482,6 +1500,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1490,6 +1509,16 @@ select * from outparam_succeed(int4range(1,2));
[1,2) | foo
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1498,6 +1527,7 @@ select * from inoutparam_succeed(int4range(1,2));
2 | [1,2)
(1 row)
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
select * from table_succeed(123, int4range(1,11));
@@ -1510,14 +1540,14 @@ select * from table_succeed(123, int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 192445878d..d9ce961be2 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
numrange_test|t
onek|t
onek2|t
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index cd7fc03b04..60a9b4d22f 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -193,18 +193,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -238,17 +239,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -316,18 +318,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -361,17 +364,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -629,3 +633,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
+ rngtypid | rngsubtype | rngmultitypid
+----------+------------+---------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index d2b17dd3ea..5355da7b01 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index acba391332..1f677f63af 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..0f70919e1a
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,630 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+select int4range(1, 3)::int4multirange;
+select int4range(1, null)::int4multirange;
+select int4range(null, null)::int4multirange;
+select 'empty'::textrange::textmultirange;
+select textrange('a', 'c')::textmultirange;
+select textrange('a', null)::textmultirange;
+select textrange(null, null)::textmultirange;
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT nummultirange() + nummultirange();
+SELECT nummultirange() + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT nummultirange() - nummultirange();
+SELECT nummultirange() - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+SELECT nummultirange() * nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index f06f245db3..4110a56ad2 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -273,13 +284,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- Look for functions that accept cstring and are neither datatype input
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 72d80bc9d4..07c9bcf6d3 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -512,16 +516,25 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fed413bf98..2e34bc8b65 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -151,7 +151,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -180,7 +180,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -232,7 +232,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -261,7 +261,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -465,3 +465,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index e216de9570..c23ce28af1 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1354,6 +1354,9 @@ MultiXactMember
MultiXactOffset
MultiXactStateData
MultiXactStatus
+MultirangeIOData
+MultirangeParseState
+MultirangeType
MyData
NDBOX
NODE
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-14 18:13 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-03-17 04:52 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-19 20:41 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2020-03-17 04:52 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
On Sat, Mar 14, 2020 at 11:13 AM Paul A Jungwirth
<pj@illuminatedcomputing.com> wrote:
> I think that should fix the cfbot failure.
I saw this patch was failing to apply again. There was some
refactoring to how polymorphic types are determined. I added my
changes for anymultirange to that new approach, and things should be
passing again.
Yours,
Paul
Attachments:
[application/octet-stream] v14-multirange.patch (318.9K, ../../CA+renyWfVue-XQOxztE5RUOfTmffC2O6jqkmkgMcVhRUK5Y-vg@mail.gmail.com/2-v14-multirange.patch)
download | inline diff:
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 9ec1af780b..92a1a254ac 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -229,9 +229,9 @@
</indexterm>
<para>
- Five pseudo-types of special interest are <type>anyelement</type>,
+ Six pseudo-types of special interest are <type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
- and <type>anyrange</type>,
+ <type>anyrange</type>, and <type>anymultirange</type>.
which are collectively called <firstterm>polymorphic types</firstterm>.
Any function declared using these types is said to be
a <firstterm>polymorphic function</firstterm>. A polymorphic function can
@@ -250,15 +250,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type>, the actual range type in
- the <type>anyrange</type> positions must be a range whose subtype is
- the same type appearing in the <type>anyelement</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -268,6 +268,20 @@
</para>
<para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type>, the actual range type in
+ the <type>anyrange</type> positions must be a range whose subtype is
+ the same type appearing in the <type>anyelement</type> positions.
+ The type <type>anymultirange</type> accepts a multirange
+ whose contents match the other polymorphic types.
+ That is it must hold ranges matching <type>anyrange</type>
+ and base type elements matching <type>anyelement</type> (if either of
+ those are present). If <type>anyarray</type> is present its elements
+ must match the base type of the <type>anyrange</type> and/or the base
+ type of the ranges in an <type>anymultirange</type>.
+ </para>
+
+ <para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
types are allowed. For example, a function declared as
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index fc4d7f0f78..45cabfd7e2 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -13546,7 +13546,7 @@ SELECT NULLIF(value, '(none)') ...
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14017,12 +14017,14 @@ NULL baz</literallayout>(3 rows)</entry>
<title>Range Functions and Operators</title>
<para>
- See <xref linkend="rangetypes"/> for an overview of range types.
+ See <xref linkend="rangetypes"/> for an overview of range and multirange types.
</para>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14040,134 +14042,215 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
- <entry><literal>f</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">f</literallayout></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
- <entry><literal>[5,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)
+'{[5,10)}'::nummultirange + '{[15,20)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,20)
+{[5,10), [15,20)}</literallayout></entry>
</row>
<row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
- <entry><literal>[10,15)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)
+'{[5,15)}'::int8multirange * '{[10,20)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[10,15)
+{[10,15)}</literallayout></entry>
</row>
<row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
- <entry><literal>[5,10)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)
+'{[5,20)}'::int8multirange - '{[10,15)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,10)
+{[5,10), [15,20)}</literallayout></entry>
</row>
</tbody>
@@ -14185,19 +14268,31 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. There are separate operators for union and difference that take
+ multirange parameters and return a multirange, and they do not fail even if
+ their arguments are disjoint. So if you need a union or difference operation
+ for ranges that may be disjoint, you can avoid errors by first casting your
+ ranges to multiranges.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14249,6 +14344,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14260,6 +14366,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>isempty</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14271,6 +14388,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14282,6 +14410,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14293,6 +14432,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14304,6 +14454,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14315,6 +14476,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>range_merge</function>(<type>anyrange</type>, <type>anyrange</type>)
</literal>
</entry>
@@ -14323,16 +14495,38 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
+ <function>multirange</function>(<type>anyrange</type>)
+ </literal>
+ </entry>
+ <entry><type>anymultirange</type></entry>
+ <entry>a multirange containing just the given range</entry>
+ <entry><literal>multirange('[1,2)'::int4range)</literal></entry>
+ <entry><literal>{[1,2)}</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -14654,6 +14848,44 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>string_agg</primary>
</indexterm>
<function>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index b75fb3a392..c8784bdce3 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -232,10 +239,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -269,6 +296,19 @@ SELECT int8range(1, 14, '(]');
SELECT numrange(NULL, 2.2);
</programlisting>
</para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
+</programlisting>
+ </para>
</sect2>
<sect2 id="rangetypes-discrete">
@@ -342,6 +382,11 @@ SELECT '[1.234, 5.678]'::floatrange;
</para>
<para>
+ When you define your own range you automatically get a corresponding
+ multirange type.
+ </para>
+
+ <para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
ordering that determines which values fall into a given range.
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 423fd79d94..fb375cd3bf 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -192,6 +192,7 @@ ProcedureCreate(const char *procedureName,
genericInParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericInParam = true;
anyrangeInParam = true;
break;
@@ -219,6 +220,7 @@ ProcedureCreate(const char *procedureName,
genericOutParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericOutParam = true;
anyrangeOutParam = true;
break;
@@ -231,10 +233,10 @@ ProcedureCreate(const char *procedureName,
/*
* Do not allow polymorphic return type unless at least one input argument
- * is polymorphic. ANYRANGE return type is even stricter: must have an
- * ANYRANGE input (since we can't deduce the specific range type from
- * ANYELEMENT). Also, do not allow return type INTERNAL unless at least
- * one input argument is INTERNAL.
+ * is polymorphic. ANYRANGE and ANYMULTIRANGE return types are even
+ * stricter: must have an ANYRANGE or ANYMULTIRANGE input (since we can't
+ * deduce the specific range type from ANYELEMENT). Also, do not allow
+ * return type INTERNAL unless at least one input argument is INTERNAL.
*/
if ((IsPolymorphicType(returnType) || genericOutParam)
&& !genericInParam)
@@ -243,12 +245,13 @@ ProcedureCreate(const char *procedureName,
errmsg("cannot determine result data type"),
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
- if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
- !anyrangeInParam)
+ if ((returnType == ANYRANGEOID ||
+ returnType == ANYMULTIRANGEOID ||
+ anyrangeOutParam) && !anyrangeInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
- errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
+ errdetail("A function returning \"anyrange\" or \"anymultirange\" must have at least one \"anyrange\" or \"anymultirange\" argument.")));
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
ereport(ERROR,
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index b5bc36c2bd..1217a6ddd0 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,12 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index cd56714968..c359f27c74 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -27,6 +27,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
@@ -36,6 +37,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
+ bool tryOriginal);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -809,31 +813,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
char *
makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
- char *arr = (char *) palloc(NAMEDATALEN);
- int namelen = strlen(typeName);
- int i;
-
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
+ char *arr;
- if (i >= NAMEDATALEN - 1)
+ arr = makeUniqueTypeName(typeName, typeNamespace, false);
+ if (arr == NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -904,3 +887,91 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char *buf;
+ char *mrname;
+ char *rangestr;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "_multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ char *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
+
+ buf = psprintf("%s%s%s", prefix, "multi", rangestr);
+ }
+ else
+ buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
+
+ /* clip it at NAMEDATALEN-1 bytes */
+ buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
+
+ mrname = makeUniqueTypeName(buf, typeNamespace, true);
+ if (mrname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mrname;
+}
+
+/*
+ * makeUniqueTypeName
+ * Generate a unique name for a prospective new type
+ *
+ * Given a typeName, return a new palloc'ed name by preprending underscores
+ * until a non-conflicting name results.
+ *
+ * If tryOriginal, first try with zero underscores.
+ */
+static char *
+makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
+{
+ int i;
+ int namelen;
+ char dest[NAMEDATALEN];
+
+ Assert(strlen(typeName) <= NAMEDATALEN - 1);
+
+ if (tryOriginal &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(typeName);
+
+ /*
+ * The idea is to prepend underscores as needed until we make a name that
+ * doesn't collide with anything ...
+ */
+ namelen = strlen(typeName);
+ for (i = 1; i < NAMEDATALEN - 1; i++)
+ {
+ dest[i - 1] = '_';
+ strlcpy(dest + i, typeName, NAMEDATALEN - i);
+ if (namelen + i >= NAMEDATALEN)
+ truncate_identifier(dest, NAMEDATALEN, false);
+
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(dest);
+ }
+
+ return NULL;
+}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 8891b1d564..dfb2edd3d0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -105,9 +105,14 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid,
+ Oid rangeArrayOid, Oid *castFuncOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -738,7 +743,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1279,6 +1285,11 @@ checkEnumOwner(HeapTuple tup)
/*
* DefineRange
* Registers a new range type.
+ *
+ * Perhaps it might be worthwhile to set pg_type.typelem to the base type,
+ * and likewise on multiranges to set it to the range type. But having a
+ * non-zero typelem is treated elsewhere as a synonym for being an array,
+ * and users might have queries with that same assumption.
*/
ObjectAddress
DefineRange(CreateRangeStmt *stmt)
@@ -1287,7 +1298,11 @@ DefineRange(CreateRangeStmt *stmt)
Oid typeNamespace;
Oid typoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1304,6 +1319,8 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
+ Oid castFuncOid;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1452,8 +1469,10 @@ DefineRange(CreateRangeStmt *stmt)
/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for ranges */
alignment = (subtypalign == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
- /* Allocate OID for array type */
+ /* Allocate OID for array type, its multirange, and its multirange array */
rangeArrayOid = AssignTypeArrayOid();
+ multirangeOid = AssignTypeMultirangeOid();
+ multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
address =
@@ -1491,9 +1510,46 @@ DefineRange(CreateRangeStmt *stmt)
Assert(typoid == InvalidOid || typoid == address.objectId);
typoid = address.objectId;
+ /* Create the multirange that goes with it */
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(multirangeOid, /* force assignment of this type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_RANGE, /* type-category (range type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ Assert(multirangeOid == mltrngaddress.objectId);
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
@@ -1534,8 +1590,53 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ multirangeOid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ multirangeOid, typoid, rangeArrayOid,
+ &castFuncOid);
+
+ /* Create cast from the range type to its multirange type */
+ CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1613,6 +1714,147 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ *
+ * Sets castFuncOid to the oid of the new constructor that can be used
+ * to cast from a range to a multirange.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
+ Oid *castFuncOid)
+{
+ ObjectAddress myself,
+ referenced;
+ oidvector *argtypes;
+ Datum allParamTypes;
+ ArrayType *allParameterTypes;
+ Datum paramModes;
+ ArrayType *parameterModes;
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ /* 0-arg constructor - for empty multiranges */
+ argtypes = buildoidvector(NULL, 0);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor0", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /*
+ * Make the constructor internally-dependent on the multirange type so
+ * that they go away silently when the type is dropped. Note that pg_dump
+ * depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+
+ /*
+ * 1-arg constructor - for casts
+ *
+ * In theory we shouldn't need both this and the vararg (n-arg) constructor,
+ * but having a separate 1-arg function lets us define casts against it.
+ */
+ argtypes = buildoidvector(&rangeOid, 1);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor1", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ true, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ *castFuncOid = myself.objectId;
+
+ /* n-arg constructor - vararg */
+ argtypes = buildoidvector(&rangeArrayOid, 1);
+ allParamTypes = ObjectIdGetDatum(rangeArrayOid);
+ allParameterTypes = construct_array(&allParamTypes,
+ 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
+ parameterModes = construct_array(¶mModes, 1, CHAROID,
+ 1, true, 'c');
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor2", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(allParameterTypes), /* allParameterTypes */
+ PointerGetDatum(parameterModes), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ pfree(allParameterTypes);
+ pfree(parameterModes);
+}
/*
* Find suitable I/O functions for a type.
@@ -2045,6 +2287,72 @@ AssignTypeArrayOid(void)
return type_array_oid;
}
+/*
+ * AssignTypeMultirangeOid
+ *
+ * Pre-assign the range type's multirange OID for use in pg_type.oid
+ */
+Oid
+AssignTypeMultirangeOid(void)
+{
+ Oid type_multirange_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
+
+ type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
+ binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_oid;
+}
+
+/*
+ * AssignTypeMultirangeArrayOid
+ *
+ * Pre-assign the range type's multirange array OID for use in pg_type.typarray
+ */
+Oid
+AssignTypeMultirangeArrayOid(void)
+{
+ Oid type_multirange_array_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
+
+ type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
+ binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_array_oid;
+}
+
/*-------------------------------------------------------------------
* DefineCompositeType
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 4a2b463d1b..96bb9f0232 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -185,18 +185,20 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
- targetTypeId == ANYRANGEOID)
+ targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call anyarray_in,
- * anyenum_in, or anyrange_in, which will produce an error. Also, if
- * what we have is a domain over array, enum, or range, we have to
- * relabel it to its base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * anyarray_in, anyenum_in, anyrange_in, or anymultirange_in, which
+ * will produce an error. Also, if what we have is a domain over
+ * array, enum, range, or multirange, we have to relabel it to its
+ * base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1479,6 +1481,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid array_typelem;
Oid range_typeid = InvalidOid;
Oid range_typelem;
+ Oid multirange_typeid = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
@@ -1525,6 +1529,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1575,6 +1588,41 @@ check_generic_type_consistency(const Oid *actual_arg_types,
/* otherwise, they better match */
return false;
}
+ else
+ range_typelem = InvalidOid; /* keep compiler quiet */
+ }
+ else
+ range_typelem = InvalidOid; /* keep compiler quiet */
+
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_range_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
}
if (have_anynonarray)
@@ -1629,10 +1677,11 @@ check_generic_type_consistency(const Oid *actual_arg_types,
* 5) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the
* argument's actual type as the function's return type.
* 6) If return type is ANYELEMENT, no argument is ANYELEMENT, but any argument
- * is ANYARRAY or ANYRANGE, use the actual type of the argument to determine
- * the function's return type, i.e. the array type's corresponding element
- * type or the range type's corresponding subtype (or both, in which case
- * they must match).
+ * is ANYARRAY or ANYRANGE or ANYMULTIRANGE, use the actual type of the
+ * argument to determine the function's return type, i.e. the array type's
+ * corresponding element type or the range type's corresponding subtype
+ * or the multirange type's range type's corresponding subtype (or more than
+ * one, in which case they must match).
* 7) If return type is ANYELEMENT, no argument is ANYELEMENT, ANYARRAY, or
* ANYRANGE, generate an error. (This condition is prevented by CREATE
* FUNCTION and therefore is not expected here.)
@@ -1678,13 +1727,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid array_typelem;
- Oid range_typelem;
+ Oid range_typelem = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
+ bool have_anyrange = (rettype == ANYRANGEOID);
+ bool have_anymultirange = (rettype == ANYMULTIRANGEOID);
/*
* Loop through the arguments to see if we have any that are polymorphic.
@@ -1742,7 +1795,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else if (decl_type == ANYRANGEOID)
{
- have_generics = true;
+ have_generics = have_anyrange = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
@@ -1760,6 +1813,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ have_generics = have_anymultirange = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
}
/*
@@ -1810,9 +1883,12 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- if (range_typeid == ANYRANGEOID && !have_anyelement)
+ if (range_typeid == ANYRANGEOID && !have_anyelement && !have_anymultirange)
{
- /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
+ /*
+ * Special case for ANYRANGE input: okay iff no ANYELEMENT or
+ * ANYMULTIRANGE
+ */
range_typelem = ANYELEMENTOID;
}
else
@@ -1844,6 +1920,71 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(range_typeid),
format_type_be(elem_typeid))));
}
+ else
+ range_typelem = InvalidOid;
+ }
+
+ /* Get the range type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ if (multirange_typeid == ANYMULTIRANGEOID && !have_anyrange && !have_anyelement)
+ {
+ /*
+ * Special case for ANYMULTIRANGE input: okay iff no ANYRANGE or
+ * ANYELEMENT
+ */
+ multirange_typelem = ANYRANGEOID;
+ }
+ else
+ {
+ multirange_typelem = get_range_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+ }
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ /*
+ * Infer the element type too if needed (if there is an ANYMULTIRANGE
+ * and ANYELEMENT, with no ANYRANGE).
+ */
+ if (!OidIsValid(elem_typeid))
+ {
+ elem_typeid = get_range_subtype(range_typeid);
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
}
if (!OidIsValid(elem_typeid))
@@ -1853,6 +1994,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -1925,6 +2067,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -1956,6 +2109,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
@@ -1966,7 +2135,6 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return rettype;
}
-
/* TypeCategory()
* Assign a category to the specified type OID.
*
@@ -2069,6 +2237,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..7118dd0034 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..bd3cf3dc93
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,2186 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ FmgrInfo typioproc; /* range type's I/O proc */
+ Oid typioparam; /* range type's I/O parameter */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData *get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks either either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str = NULL;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */ ;
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **)
+ repalloc(ranges, range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(InputFunctionCall(&cache->typioproc,
+ range_str_copy,
+ cache->typioparam,
+ typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->typioproc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(ReceiveFunctionCall(&cache->typioproc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range;
+
+ range = RangeTypePGetDatum(ranges[i]);
+ range = PointerGetDatum(SendFunctionCall(&cache->typioproc, range));
+
+ pq_sendint32(buf, VARSIZE(range) - VARHDRSZ);
+ pq_sendbytes(buf, VARDATA(range), VARSIZE(range) - VARHDRSZ);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ Oid typiofunc;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &typiofunc);
+
+ if (!OidIsValid(typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(typiofunc, &cache->typioproc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ bytelen += MAXALIGN(VARSIZE(ranges[i]));
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ memcpy(ptr, ranges[i], VARSIZE(ranges[i]));
+ ptr += MAXALIGN(VARSIZE(ranges[i]));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of arbitrary ranges into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ *
+ * Returns the number of slots actually used, which may be less than
+ * input_range_count but never more.
+ *
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType *multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor2(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_CARDINALITY_VIOLATION),
+ errmsg("multiranges cannot be constructed from multi-dimensional arrays")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Construct multirange value from a single range.
+ * It'd be nice if we could just use multirange_constructor2
+ * for this case, but we need a non-variadic single-arg function
+ * to let us define a CAST from a range to its multirange.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ RangeType *range;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ range = PG_GETARG_RANGE_P(0);
+
+ /* Make sure the range type matches. */
+ rngtypid = RangeTypeGetOid(range);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 1, &range));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ elog(ERROR, /* can't happen */
+ "niladic multirange constructor must not receive arguments");
+}
+
+
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+multirange_union(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
+}
+
+/* multirange minus */
+Datum
+multirange_minus(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+multirange_intersect(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+ /*-----------------------------------------------
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1,
+ * but one extra won't hurt.
+ *-----------------------------------------------
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType *mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges1[range_count1 - 1],
+ ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype,
+ ranges1[0],
+ ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1, MultirangeType *mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
+ return false;
+
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
+ return false;
+ }
+
+ /* All ranges in mr2 are satisfied */
+ return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 18f2ee8226..248ea7f44a 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -52,6 +52,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
}
Datum
+binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
+binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_array_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
binary_upgrade_set_next_toast_pg_type_oid(PG_FUNCTION_ARGS)
{
Oid typoid = PG_GETARG_OID(0);
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 9eee03c143..731efb8f98 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -195,6 +196,54 @@ anyrange_out(PG_FUNCTION_ARGS)
}
/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
+/*
+ * anyrangearray_in - input routine for pseudo-type ANYRANGEARRAY.
+ */
+Datum
+anyrangearray_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anyrangearray")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anyrangearray_out - output routine for pseudo-type ANYRANGEARRAY.
+ *
+ * We may as well allow this, since array_out will in fact work.
+ */
+Datum
+anyrangearray_out(PG_FUNCTION_ARGS)
+{
+ return array_out(fcinfo);
+}
+
+/*
* void
*
* We support void_in so that PL functions can return VOID without any
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 01ad8bc240..0af3c67296 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -431,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -452,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -475,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -485,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -495,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -505,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -515,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -957,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -969,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -993,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1101,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1110,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1132,9 +1180,81 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
+}
+
+/* range, range -> range, range functions */
+
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
+ {
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
+ }
+
+ return false;
}
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
+}
+
+
/* Btree support */
/* btree comparator */
@@ -1144,12 +1264,6 @@ range_cmp(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
- RangeBound lower1,
- lower2;
- RangeBound upper1,
- upper2;
- bool empty1,
- empty2;
int cmp;
check_stack_depth(); /* recurses when subtype is a range type */
@@ -1160,6 +1274,28 @@ range_cmp(PG_FUNCTION_ARGS)
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+ cmp = range_cmp_internal(typcache, r1, r2);
+
+ PG_FREE_IF_COPY(r1, 0);
+ PG_FREE_IF_COPY(r2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
@@ -1177,10 +1313,7 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
- PG_FREE_IF_COPY(r1, 0);
- PG_FREE_IF_COPY(r2, 1);
-
- PG_RETURN_INT32(cmp);
+ return cmp;
}
/* inequality operators using the range_cmp function */
@@ -1218,13 +1351,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1363,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1404,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1433,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1471,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1770,6 +1917,21 @@ range_get_flags(const RangeType *range)
}
/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
+/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
* This is only needed in GiST operations, so we don't include a provision
@@ -1938,6 +2100,46 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
}
/*
+ * qsort callback for sorting ranges.
+ *
+ * Two empty ranges compare equal; an empty range sorts to the left of any
+ * non-empty range. Two non-empty ranges are sorted by lower bound first
+ * and by upper bound next.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
+/*
* Build an empty range value of the type indicated by the typcache entry.
*/
RangeType *
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 27bbb58f56..c6c079c623 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2495,6 +2495,16 @@ type_is_range(Oid typid)
}
/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
+/*
* get_type_category_preferred
*
* Given the type OID, fetch its category and preferred-type status.
@@ -3149,7 +3159,7 @@ get_namespace_name_or_temp(Oid nspid)
return get_namespace_name(nspid);
}
-/* ---------- PG_RANGE CACHE ---------- */
+/* ---------- PG_RANGE CACHES ---------- */
/*
* get_range_subtype
@@ -3202,6 +3212,56 @@ get_range_collation(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngmultitypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_range_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_range_multirange_subtype(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGEMULTIRANGE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 53d9ddf159..7654331a59 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -651,6 +651,18 @@ static const struct cachedesc cacheinfo[] = {
},
64
},
+ {RangeRelationId, /* RANGEMULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngmultitypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+
{RangeRelationId, /* RANGETYPE */
RangeTypidIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 854f133f9b..160846cf10 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -286,6 +286,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -302,6 +303,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -553,8 +557,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -591,7 +595,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -616,7 +620,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -641,7 +645,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -693,6 +697,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -737,6 +748,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -817,6 +835,16 @@ lookup_type_cache(Oid type_id, int flags)
}
/*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
+ /*
* If requested, get information about a domain type
*/
if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
@@ -927,6 +955,22 @@ load_rangetype_info(TypeCacheEntry *typentry)
typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
}
+/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Oid rangetypeOid;
+
+ rangetypeOid = get_range_multirange_subtype(typentry->type_id);
+ if (!OidIsValid(rangetypeOid))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
/*
* load_domaintype_info --- helper routine to set up domain constraint info
@@ -1527,11 +1571,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1574,6 +1618,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 4e9d21bd54..efefc06d15 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -35,6 +35,7 @@ typedef struct polymorphic_actuals
Oid anyelement_type; /* anyelement mapping, if known */
Oid anyarray_type; /* anyarray mapping, if known */
Oid anyrange_type; /* anyrange mapping, if known */
+ Oid anymultirange_type; /* anymultirange mapping, if known */
} polymorphic_actuals;
static void shutdown_MultiFuncCall(Datum arg);
@@ -46,6 +47,7 @@ static TypeFuncClass internal_get_result_type(Oid funcid,
static void resolve_anyelement_from_others(polymorphic_actuals *actuals);
static void resolve_anyarray_from_others(polymorphic_actuals *actuals);
static void resolve_anyrange_from_others(polymorphic_actuals *actuals);
+static void resolve_anymultirange_from_others(polymorphic_actuals *actuals);
static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
oidvector *declared_args,
Node *call_expr);
@@ -503,6 +505,31 @@ resolve_anyelement_from_others(polymorphic_actuals *actuals)
format_type_be(range_base_type))));
actuals->anyelement_type = range_typelem;
}
+ else if (OidIsValid(actuals->anymultirange_type))
+ {
+ /* Use the element type based on the multirange type */
+ Oid multirange_base_type = getBaseType(actuals->anymultirange_type);
+ Oid multirange_typelem =
+ get_range_multirange_subtype(multirange_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_base_type))));
+
+ Oid range_base_type = getBaseType(multirange_typelem);
+ Oid range_typelem = get_range_subtype(range_base_type);
+
+ if (!OidIsValid(range_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s does not contain a range type but type %s",
+ "anymultirange",
+ format_type_be(range_base_type))));
+ actuals->anyelement_type = range_typelem;
+ }
else
elog(ERROR, "could not determine polymorphic type");
}
@@ -540,10 +567,54 @@ static void
resolve_anyrange_from_others(polymorphic_actuals *actuals)
{
/*
- * We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types with the same subtype.
+ * We can't deduce a range type from other polymorphic array or base types,
+ * because there may be multiple range types with the same subtype, but we
+ * can deduce it from a polymorphic multirange type.
+ */
+ if (OidIsValid(actuals->anymultirange_type))
+ {
+ /* Use the element type based on the multirange type */
+ Oid multirange_base_type = getBaseType(actuals->anymultirange_type);
+ Oid multirange_typelem =
+ get_range_multirange_subtype(multirange_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_base_type))));
+ actuals->anyrange_type = multirange_typelem;
+ }
+ else
+ elog(ERROR, "could not determine polymorphic type");
+}
+
+/*
+ * Resolve actual type of ANYMULTIRANGE from other polymorphic inputs
+ */
+static void
+resolve_anymultirange_from_others(polymorphic_actuals *actuals)
+{
+ /*
+ * We can't deduce a multirange type from polymorphic array or base types,
+ * because there may be multiple range types with the same subtype, but we
+ * can deduce it from a polymorphic range type.
*/
- elog(ERROR, "could not determine polymorphic type");
+ if (OidIsValid(actuals->anyrange_type))
+ {
+ Oid range_base_type = getBaseType(actuals->anyrange_type);
+ Oid multirange_typeid = get_range_multirange(range_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(actuals->anyrange_type))));
+ actuals->anymultirange_type = multirange_typeid;
+ }
+ else
+ elog(ERROR, "could not determine polymorphic type");
}
/*
@@ -564,6 +635,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
polymorphic_actuals poly_actuals;
Oid anycollation = InvalidOid;
int i;
@@ -587,6 +659,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
have_polymorphic_result = true;
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_polymorphic_result = true;
+ have_anymultirange_result = true;
+ break;
default:
break;
}
@@ -636,6 +712,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
}
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ {
+ poly_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ return false;
+ }
+ break;
default:
break;
}
@@ -651,6 +736,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&poly_actuals);
+
/*
* Identify the collation to use for polymorphic OUT parameters. (It'll
* necessarily be the same for both anyelement and anyarray.) Note that
@@ -708,6 +796,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ poly_actuals.anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
default:
break;
}
@@ -735,6 +831,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
polymorphic_actuals poly_actuals;
int inargno;
int i;
@@ -808,6 +905,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = poly_actuals.anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ have_anymultirange_result = true;
+ else
+ {
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ {
+ poly_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ return false;
+ }
+ argtypes[i] = poly_actuals.anymultirange_type;
+ }
+ break;
default:
break;
}
@@ -829,6 +941,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&poly_actuals);
+
/* And finally replace the output column types as needed */
for (i = 0; i < numargs; i++)
{
@@ -845,6 +960,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = poly_actuals.anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = poly_actuals.anymultirange_type;
+ break;
default:
break;
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index ced0681ec3..f8567ad9a2 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -271,7 +271,8 @@ static void dumpSearchPath(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type);
+ bool force_array_type,
+ bool include_multirange_type);
static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
static void binary_upgrade_set_pg_class_oids(Archive *fout,
@@ -1580,7 +1581,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4340,16 +4341,49 @@ append_depends_on_extension(Archive *fout,
}
}
+static Oid
+get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+{
+ /*
+ * If the old version didn't assign an array type, but the new version
+ * does, we must select an unused type OID to assign. This currently only
+ * happens for domains, when upgrading pre-v11 to v11 and up.
+ *
+ * Note: local state here is kind of ugly, but we must have some, since we
+ * mustn't choose the same unused OID more than once.
+ */
+ static Oid next_possible_free_oid = FirstNormalObjectId;
+ PGresult *res;
+ bool is_dup;
+
+ do
+ {
+ ++next_possible_free_oid;
+ printfPQExpBuffer(upgrade_query,
+ "SELECT EXISTS(SELECT 1 "
+ "FROM pg_catalog.pg_type "
+ "WHERE oid = '%u'::pg_catalog.oid);",
+ next_possible_free_oid);
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+ is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
+ PQclear(res);
+ } while (is_dup);
+
+ return next_possible_free_oid;
+}
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type)
+ bool force_array_type,
+ bool include_multirange_type)
{
PQExpBuffer upgrade_query = createPQExpBuffer();
PGresult *res;
Oid pg_type_array_oid;
+ Oid pg_type_multirange_oid;
+ Oid pg_type_multirange_array_oid;
appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
@@ -4370,33 +4404,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQclear(res);
if (!OidIsValid(pg_type_array_oid) && force_array_type)
- {
- /*
- * If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
- *
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
- */
- static Oid next_possible_free_oid = FirstNormalObjectId;
- bool is_dup;
-
- do
- {
- ++next_possible_free_oid;
- printfPQExpBuffer(upgrade_query,
- "SELECT EXISTS(SELECT 1 "
- "FROM pg_catalog.pg_type "
- "WHERE oid = '%u'::pg_catalog.oid);",
- next_possible_free_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
- is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
- PQclear(res);
- } while (is_dup);
-
- pg_type_array_oid = next_possible_free_oid;
- }
+ pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
if (OidIsValid(pg_type_array_oid))
{
@@ -4407,6 +4415,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
pg_type_array_oid);
}
+ /*
+ * Pre-set the multirange type oid and its own array type oid.
+ */
+ if (include_multirange_type)
+ {
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBuffer(upgrade_query,
+ "SELECT t.oid, t.typarray "
+ "FROM pg_catalog.pg_type t "
+ "JOIN pg_catalog.pg_range r "
+ "ON t.oid = r.rngmultitypid "
+ "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
+ pg_type_oid);
+
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+
+ pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+ pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
+
+ PQclear(res);
+ }
+ else
+ {
+ pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ }
+
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_oid);
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_array_oid);
+ }
+
destroyPQExpBuffer(upgrade_query);
}
@@ -4440,7 +4488,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
- pg_type_oid, false);
+ pg_type_oid, false, false);
if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
{
@@ -4989,6 +5037,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
@@ -8103,9 +8156,12 @@ getProcLangs(Archive *fout, int *numProcLangs)
/*
* getCasts
- * get basic information about every cast in the system
+ * get basic information about most casts in the system
*
* numCasts is set to the number of casts read in
+ *
+ * Skip casts from a range to its multirange, since we'll create those
+ * automatically.
*/
CastInfo *
getCasts(Archive *fout, int *numCasts)
@@ -8123,7 +8179,20 @@ getCasts(Archive *fout, int *numCasts)
int i_castcontext;
int i_castmethod;
- if (fout->remoteVersion >= 80400)
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBufferStr(query, "SELECT tableoid, oid, "
+ "castsource, casttarget, castfunc, castcontext, "
+ "castmethod "
+ "FROM pg_cast c "
+ "WHERE NOT EXISTS ( "
+ "SELECT 1 FROM pg_range r "
+ "WHERE c.castsource = r.rngtypid "
+ "AND c.casttarget = r.rngmultitypid "
+ ") "
+ "ORDER BY 3,4");
+ }
+ else if (fout->remoteVersion >= 80400)
{
appendPQExpBufferStr(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
@@ -10311,7 +10380,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
qualtypname);
@@ -10437,7 +10506,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, true);
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
qualtypname);
@@ -10543,7 +10612,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
qualtypname);
@@ -10748,7 +10817,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
@@ -10935,7 +11004,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- true); /* force array type */
+ true, /* force array type */
+ false); /* force multirange type */
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -11123,7 +11193,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
{
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
}
@@ -11397,7 +11467,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
stinfo->baseType->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
fmtQualifiedDumpable(stinfo));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index e0c6444ef6..90a93b4cb2 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -176,6 +176,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 70157cf90a..c262265b95 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -139,8 +139,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 12d94fe1b3..01d95117cd 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,6 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 8be303870f..566f9364c5 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -327,6 +327,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_rngmultitypid_index, 8001, on pg_range using btree(rngmultitypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index ffabe275c0..03bab74253 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..827ac4f03c 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 75c0152b66..cf0cac5641 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -285,6 +285,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
@@ -445,6 +447,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
@@ -1263,12 +1270,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index 6ef8b8a4e7..14277c8fdf 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -512,4 +512,17 @@
{ castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
castcontext => 'e', castmethod => 'f' },
+# range to multirange
+{ castsource => 'int4range', casttarget => 'int4multirange', castfunc => 'int4multirange(int4range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'int8range', casttarget => 'int8multirange', castfunc => 'int8multirange(int8range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'numrange', casttarget => 'nummultirange', castfunc => 'nummultirange(numrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'daterange', casttarget => 'datemultirange', castfunc => 'datemultirange(daterange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tsrange', casttarget => 'tsmultirange', castfunc => 'tsmultirange(tsrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tstzrange', casttarget => 'tstzmultirange', castfunc => 'tstzmultirange(tstzrange)',
+ castcontext => 'e', castmethod => 'f' },
]
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index ab2f50c9eb..eebebbcdb4 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 7c135da3b1..24f4a32203 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3297,5 +3297,174 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'contsel', oprjoin => 'contjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 26227df216..901fd7b303 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 7fb574f9dc..27f25d989b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9643,6 +9643,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9692,6 +9696,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9760,6 +9771,258 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8114',
+ proname => 'multirange_union', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union' },
+{ oid => '8120',
+ proname => 'multirange_minus', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus' },
+{ oid => '8126',
+ proname => 'multirange_intersect', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8146', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 't',
+ prorettype => 'int4multirange', proargtypes => 'int4range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8147', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 't',
+ prorettype => 'nummultirange', proargtypes => 'numrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8148', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 't',
+ prorettype => 'tsmultirange', proargtypes => 'tsrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8149', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 't',
+ prorettype => 'tstzmultirange', proargtypes => 'tstzrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8150', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 't',
+ prorettype => 'datemultirange', proargtypes => 'daterange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8151', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 't',
+ prorettype => 'int8multirange', proargtypes => 'int8range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8152', descr => 'anymultirange cast',
+ proname => 'multirange', proisstrict => 't',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
@@ -10142,6 +10405,14 @@
proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
+{ oid => '8144', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
+{ oid => '8145', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
{ oid => '3585', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index 479754c245..10060255c9 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ rngmultitypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', rngmultitypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', rngmultitypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', rngmultitypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ rngmultitypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ rngmultitypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index 98172bb1b6..60a8fde518 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -34,6 +34,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* OID of range's element type (subtype) */
Oid rngsubtype BKI_LOOKUP(pg_type);
+ /* OID of the range's multirange type */
+ Oid rngmultitypid BKI_LOOKUP(pg_type);
+
/* collation for this range type, or 0 */
Oid rngcollation BKI_DEFAULT(0);
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index b00597d6ff..6e1cb796f1 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -486,6 +486,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -590,5 +624,10 @@
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 97890946c5..d18f19f55a 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -263,6 +263,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -299,7 +300,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#endif /* EXPOSE_TO_CLIENT_CODE */
@@ -358,4 +360,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 0162bc2ffe..80e3a5e399 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
+extern Oid AssignTypeMultirangeOid(void);
+extern Oid AssignTypeMultirangeArrayOid(void);
extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 4e646c55e9..80410dcd04 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -155,6 +155,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -181,6 +182,8 @@ extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
extern Oid get_range_collation(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_range_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
extern bool get_index_isreplident(Oid index_oid);
extern bool get_index_isvalid(Oid index_oid);
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..469444776c
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern MultirangeType *multirange_minus_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType *multirange_intersect_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType *range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType *make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType *make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 0cbbf09033..204aee4054 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
@@ -92,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -115,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -125,6 +141,7 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
@@ -132,8 +149,12 @@ extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f27b73d76d..e8f393520a 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -79,6 +79,7 @@ enum SysCacheIdentifier
PUBLICATIONOID,
PUBLICATIONREL,
PUBLICATIONRELMAP,
+ RANGEMULTIRANGE,
RANGETYPE,
RELNAMENSP,
RELOID,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index cdd20e56d7..33f332a141 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -101,6 +101,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
/*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype; /* multirange's range underlying type */
+
+ /*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
*/
@@ -143,6 +148,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index c8e43e684f..2d59021d68 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -511,6 +511,8 @@ do_compile(FunctionCallInfo fcinfo,
rettypeid = INT4ARRAYOID;
else if (rettypeid == ANYRANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2087,6 +2089,7 @@ build_datatype(HeapTuple typeTup, int32 typmod,
case TYPTYPE_BASE:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
+ case TYPTYPE_MULTIRANGE:
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_COMPOSITE:
@@ -2501,6 +2504,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYRANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..8232795148 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -140,6 +140,7 @@ owner of sequence deptest_a_seq
owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
+owner of type deptest_multirange
owner of type deptest_range
owner of table deptest2
owner of sequence ss1
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..723f2408b0
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,2395 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+ int4multirange
+----------------
+ {}
+(1 row)
+
+select int4range(1, 3)::int4multirange;
+ int4range
+-----------
+ {[1,3)}
+(1 row)
+
+select int4range(1, null)::int4multirange;
+ int4range
+-----------
+ {[1,)}
+(1 row)
+
+select int4range(null, null)::int4multirange;
+ int4range
+-----------
+ {(,)}
+(1 row)
+
+select 'empty'::textrange::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textrange('a', 'c')::textmultirange;
+ textrange
+-----------
+ {[a,c)}
+(1 row)
+
+select textrange('a', null)::textmultirange;
+ textrange
+-----------
+ {[a,)}
+(1 row)
+
+select textrange(null, null)::textmultirange;
+ textrange
+-----------
+ {(,)}
+(1 row)
+
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT nummultirange() + nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT nummultirange() - nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() * nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+ mr_polymorphic
+----------------
+ {[1,4)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 40468e8f49..1514f3af7c 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -206,6 +213,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -224,6 +233,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -329,13 +340,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
@@ -343,16 +355,19 @@ ORDER BY 2;
2502 | anyarray_recv
2312 | anyelement_in
3504 | anyenum_in
+ 8002 | anymultirange_in
2777 | anynonarray_in
3832 | anyrange_in
750 | array_in
2400 | array_recv
3506 | enum_in
3532 | enum_recv
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(13 rows)
+(16 rows)
-- Look for functions that accept cstring and are neither datatype input
-- functions nor encoding conversion functions. It's almost never a good
@@ -2139,13 +2154,14 @@ ORDER BY 1, 2, 3;
| float_ops | float4_ops | real
| float_ops | float8_ops | double precision
| jsonb_ops | jsonb_ops | jsonb
+ | multirange_ops | multirange_ops | anymultirange
| numeric_ops | numeric_ops | numeric
| range_ops | range_ops | anyrange
| record_image_ops | record_image_ops | record
| record_ops | record_ops | record
| tsquery_ops | tsquery_ops | tsquery
| tsvector_ops | tsvector_ops | tsvector
-(14 rows)
+(15 rows)
-- **************** pg_index ****************
-- Look for illegal values in pg_index fields.
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 348235a15e..3548026223 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1371,7 +1389,7 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
@@ -1482,6 +1500,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1499,6 +1518,16 @@ select * from outparam2_succeed(int4range(1,11));
{1,11} | {11,1}
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1521,14 +1550,14 @@ select * from table_succeed(int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 192445878d..d9ce961be2 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
numrange_test|t
onek|t
onek2|t
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index cd7fc03b04..60a9b4d22f 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -193,18 +193,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -238,17 +239,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -316,18 +318,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -361,17 +364,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -629,3 +633,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
+ rngtypid | rngsubtype | rngmultitypid
+----------+------------+---------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index d2b17dd3ea..5355da7b01 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index acba391332..1f677f63af 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..0f70919e1a
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,630 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+select int4range(1, 3)::int4multirange;
+select int4range(1, null)::int4multirange;
+select int4range(null, null)::int4multirange;
+select 'empty'::textrange::textmultirange;
+select textrange('a', 'c')::textmultirange;
+select textrange('a', null)::textmultirange;
+select textrange(null, null)::textmultirange;
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT nummultirange() + nummultirange();
+SELECT nummultirange() + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT nummultirange() - nummultirange();
+SELECT nummultirange() - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+SELECT nummultirange() * nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index f06f245db3..4110a56ad2 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -273,13 +284,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- Look for functions that accept cstring and are neither datatype input
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 85eaa9b34c..825f56fd88 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -512,6 +516,7 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
@@ -523,6 +528,13 @@ create function outparam2_succeed(r anyrange, out lu anyarray, out ul anyarray)
select * from outparam2_succeed(int4range(1,11));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fed413bf98..2e34bc8b65 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -151,7 +151,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -180,7 +180,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -232,7 +232,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -261,7 +261,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -465,3 +465,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index e216de9570..c23ce28af1 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1354,6 +1354,9 @@ MultiXactMember
MultiXactOffset
MultiXactStateData
MultiXactStatus
+MultirangeIOData
+MultirangeParseState
+MultirangeType
MyData
NDBOX
NODE
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-14 18:13 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-17 04:52 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-03-19 20:41 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-19 20:43 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Alvaro Herrera @ 2020-03-19 20:41 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
On 2020-Mar-16, Paul A Jungwirth wrote:
> On Sat, Mar 14, 2020 at 11:13 AM Paul A Jungwirth
> <pj@illuminatedcomputing.com> wrote:
> > I think that should fix the cfbot failure.
>
> I saw this patch was failing to apply again. There was some
> refactoring to how polymorphic types are determined. I added my
> changes for anymultirange to that new approach, and things should be
> passing again.
There's been another flurry of commits in the polymorphic types area.
Can you please rebase again?
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-14 18:13 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-17 04:52 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-19 20:41 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-03-19 20:43 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-20 05:24 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2020-03-19 20:43 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
On Thu, Mar 19, 2020 at 1:42 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
>
> On 2020-Mar-16, Paul A Jungwirth wrote:
>
> > On Sat, Mar 14, 2020 at 11:13 AM Paul A Jungwirth
> > <pj@illuminatedcomputing.com> wrote:
> > > I think that should fix the cfbot failure.
> >
> > I saw this patch was failing to apply again. There was some
> > refactoring to how polymorphic types are determined. I added my
> > changes for anymultirange to that new approach, and things should be
> > passing again.
>
> There's been another flurry of commits in the polymorphic types area.
> Can you please rebase again?
I noticed that too. :-) I'm about halfway through a rebase right now.
I can probably finish it up tonight.
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-14 18:13 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-17 04:52 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-19 20:41 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-19 20:43 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-03-20 05:24 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-23 23:32 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2020-03-20 05:24 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
On Thu, Mar 19, 2020 at 1:43 PM Paul A Jungwirth
<pj@illuminatedcomputing.com> wrote:
> On Thu, Mar 19, 2020 at 1:42 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
> > There's been another flurry of commits in the polymorphic types area.
> > Can you please rebase again?
>
> I noticed that too. :-) I'm about halfway through a rebase right now.
> I can probably finish it up tonight.
Here is that patch. I should probably add an anycompatiblemultirange
type now too? I'll get started on that tomorrow.
Regards,
Paul
Attachments:
[application/octet-stream] v15-multirange.patch (332.3K, ../../CA+renyUVUOBppNMC3Qbqa_xAi9nJdc9GSaDx6btm1pnY1eF0Ow@mail.gmail.com/2-v15-multirange.patch)
download | inline diff:
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 930aeb767c..febb02e80a 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -286,6 +286,14 @@
</row>
<row>
+ <entry><type>anymultirange</type></entry>
+ <entry>Simple</entry>
+ <entry>Indicates that a function accepts any multirange data type
+ (see <xref linkend="rangetypes"/>)
+ </entry>
+ </row>
+
+ <row>
<entry><type>anycompatible</type></entry>
<entry>Common</entry>
<entry>Indicates that a function accepts any data type,
@@ -343,17 +351,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type> or <type>anyarray</type>,
- the actual range type in the <type>anyrange</type> positions must be a
- range whose subtype is the same type appearing in
- the <type>anyelement</type> positions and the same as the element type
- of the <type>anyarray</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -363,6 +369,19 @@
</para>
<para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type> or <type>anyarray</type>,
+ the actual range type in the <type>anyrange</type> positions must be a
+ range whose subtype is the same type appearing in
+ the <type>anyelement</type> positions and the same as the element type
+ of the <type>anyarray</type> positions.
+ If there are positions declared <type>anymultirange</type>,
+ their actual multirange type must contain ranges matching parameters declared
+ <type>anyrange</type> and base elements matching parameters declared
+ <type>anyelement</type> and <type>anyarray</type>.
+ </para>
+
+ <para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
types are allowed. For example, a function declared as
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 464a48ed6a..cfcafa5b9e 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -13546,7 +13546,7 @@ SELECT NULLIF(value, '(none)') ...
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14017,12 +14017,14 @@ NULL baz</literallayout>(3 rows)</entry>
<title>Range Functions and Operators</title>
<para>
- See <xref linkend="rangetypes"/> for an overview of range types.
+ See <xref linkend="rangetypes"/> for an overview of range and multirange types.
</para>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14040,134 +14042,215 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
- <entry><literal>f</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">f</literallayout></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
- <entry><literal>[5,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)
+'{[5,10)}'::nummultirange + '{[15,20)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,20)
+{[5,10), [15,20)}</literallayout></entry>
</row>
<row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
- <entry><literal>[10,15)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)
+'{[5,15)}'::int8multirange * '{[10,20)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[10,15)
+{[10,15)}</literallayout></entry>
</row>
<row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
- <entry><literal>[5,10)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)
+'{[5,20)}'::int8multirange - '{[10,15)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,10)
+{[5,10), [15,20)}</literallayout></entry>
</row>
</tbody>
@@ -14185,19 +14268,31 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. There are separate operators for union and difference that take
+ multirange parameters and return a multirange, and they do not fail even if
+ their arguments are disjoint. So if you need a union or difference operation
+ for ranges that may be disjoint, you can avoid errors by first casting your
+ ranges to multiranges.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14249,6 +14344,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14260,6 +14366,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>isempty</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14271,6 +14388,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14282,6 +14410,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14293,6 +14432,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14304,6 +14454,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14315,6 +14476,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>range_merge</function>(<type>anyrange</type>, <type>anyrange</type>)
</literal>
</entry>
@@ -14323,16 +14495,38 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
+ <function>multirange</function>(<type>anyrange</type>)
+ </literal>
+ </entry>
+ <entry><type>anymultirange</type></entry>
+ <entry>a multirange containing just the given range</entry>
+ <entry><literal>multirange('[1,2)'::int4range)</literal></entry>
+ <entry><literal>{[1,2)}</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -14654,6 +14848,44 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>string_agg</primary>
</indexterm>
<function>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index b75fb3a392..c8784bdce3 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -232,10 +239,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -269,6 +296,19 @@ SELECT int8range(1, 14, '(]');
SELECT numrange(NULL, 2.2);
</programlisting>
</para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
+</programlisting>
+ </para>
</sect2>
<sect2 id="rangetypes-discrete">
@@ -342,6 +382,11 @@ SELECT '[1.234, 5.678]'::floatrange;
</para>
<para>
+ When you define your own range you automatically get a corresponding
+ multirange type.
+ </para>
+
+ <para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
ordering that determines which values fall into a given range.
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index b5bc36c2bd..1217a6ddd0 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,12 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index cd56714968..c359f27c74 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -27,6 +27,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
@@ -36,6 +37,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
+ bool tryOriginal);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -809,31 +813,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
char *
makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
- char *arr = (char *) palloc(NAMEDATALEN);
- int namelen = strlen(typeName);
- int i;
-
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
+ char *arr;
- if (i >= NAMEDATALEN - 1)
+ arr = makeUniqueTypeName(typeName, typeNamespace, false);
+ if (arr == NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -904,3 +887,91 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char *buf;
+ char *mrname;
+ char *rangestr;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "_multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ char *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
+
+ buf = psprintf("%s%s%s", prefix, "multi", rangestr);
+ }
+ else
+ buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
+
+ /* clip it at NAMEDATALEN-1 bytes */
+ buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
+
+ mrname = makeUniqueTypeName(buf, typeNamespace, true);
+ if (mrname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mrname;
+}
+
+/*
+ * makeUniqueTypeName
+ * Generate a unique name for a prospective new type
+ *
+ * Given a typeName, return a new palloc'ed name by preprending underscores
+ * until a non-conflicting name results.
+ *
+ * If tryOriginal, first try with zero underscores.
+ */
+static char *
+makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
+{
+ int i;
+ int namelen;
+ char dest[NAMEDATALEN];
+
+ Assert(strlen(typeName) <= NAMEDATALEN - 1);
+
+ if (tryOriginal &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(typeName);
+
+ /*
+ * The idea is to prepend underscores as needed until we make a name that
+ * doesn't collide with anything ...
+ */
+ namelen = strlen(typeName);
+ for (i = 1; i < NAMEDATALEN - 1; i++)
+ {
+ dest[i - 1] = '_';
+ strlcpy(dest + i, typeName, NAMEDATALEN - i);
+ if (namelen + i >= NAMEDATALEN)
+ truncate_identifier(dest, NAMEDATALEN, false);
+
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(dest);
+ }
+
+ return NULL;
+}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 8891b1d564..dfb2edd3d0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -105,9 +105,14 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid,
+ Oid rangeArrayOid, Oid *castFuncOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -738,7 +743,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1279,6 +1285,11 @@ checkEnumOwner(HeapTuple tup)
/*
* DefineRange
* Registers a new range type.
+ *
+ * Perhaps it might be worthwhile to set pg_type.typelem to the base type,
+ * and likewise on multiranges to set it to the range type. But having a
+ * non-zero typelem is treated elsewhere as a synonym for being an array,
+ * and users might have queries with that same assumption.
*/
ObjectAddress
DefineRange(CreateRangeStmt *stmt)
@@ -1287,7 +1298,11 @@ DefineRange(CreateRangeStmt *stmt)
Oid typeNamespace;
Oid typoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1304,6 +1319,8 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
+ Oid castFuncOid;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1452,8 +1469,10 @@ DefineRange(CreateRangeStmt *stmt)
/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for ranges */
alignment = (subtypalign == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
- /* Allocate OID for array type */
+ /* Allocate OID for array type, its multirange, and its multirange array */
rangeArrayOid = AssignTypeArrayOid();
+ multirangeOid = AssignTypeMultirangeOid();
+ multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
address =
@@ -1491,9 +1510,46 @@ DefineRange(CreateRangeStmt *stmt)
Assert(typoid == InvalidOid || typoid == address.objectId);
typoid = address.objectId;
+ /* Create the multirange that goes with it */
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(multirangeOid, /* force assignment of this type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_RANGE, /* type-category (range type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ Assert(multirangeOid == mltrngaddress.objectId);
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
@@ -1534,8 +1590,53 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ multirangeOid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ multirangeOid, typoid, rangeArrayOid,
+ &castFuncOid);
+
+ /* Create cast from the range type to its multirange type */
+ CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1613,6 +1714,147 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ *
+ * Sets castFuncOid to the oid of the new constructor that can be used
+ * to cast from a range to a multirange.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
+ Oid *castFuncOid)
+{
+ ObjectAddress myself,
+ referenced;
+ oidvector *argtypes;
+ Datum allParamTypes;
+ ArrayType *allParameterTypes;
+ Datum paramModes;
+ ArrayType *parameterModes;
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ /* 0-arg constructor - for empty multiranges */
+ argtypes = buildoidvector(NULL, 0);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor0", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /*
+ * Make the constructor internally-dependent on the multirange type so
+ * that they go away silently when the type is dropped. Note that pg_dump
+ * depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+
+ /*
+ * 1-arg constructor - for casts
+ *
+ * In theory we shouldn't need both this and the vararg (n-arg) constructor,
+ * but having a separate 1-arg function lets us define casts against it.
+ */
+ argtypes = buildoidvector(&rangeOid, 1);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor1", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ true, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ *castFuncOid = myself.objectId;
+
+ /* n-arg constructor - vararg */
+ argtypes = buildoidvector(&rangeArrayOid, 1);
+ allParamTypes = ObjectIdGetDatum(rangeArrayOid);
+ allParameterTypes = construct_array(&allParamTypes,
+ 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
+ parameterModes = construct_array(¶mModes, 1, CHAROID,
+ 1, true, 'c');
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor2", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(allParameterTypes), /* allParameterTypes */
+ PointerGetDatum(parameterModes), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ pfree(allParameterTypes);
+ pfree(parameterModes);
+}
/*
* Find suitable I/O functions for a type.
@@ -2045,6 +2287,72 @@ AssignTypeArrayOid(void)
return type_array_oid;
}
+/*
+ * AssignTypeMultirangeOid
+ *
+ * Pre-assign the range type's multirange OID for use in pg_type.oid
+ */
+Oid
+AssignTypeMultirangeOid(void)
+{
+ Oid type_multirange_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
+
+ type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
+ binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_oid;
+}
+
+/*
+ * AssignTypeMultirangeArrayOid
+ *
+ * Pre-assign the range type's multirange array OID for use in pg_type.typarray
+ */
+Oid
+AssignTypeMultirangeArrayOid(void)
+{
+ Oid type_multirange_array_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
+
+ type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
+ binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_array_oid;
+}
+
/*-------------------------------------------------------------------
* DefineCompositeType
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 645e4aa4ce..fb8c3e5cb0 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -188,6 +188,7 @@ coerce_type(ParseState *pstate, Node *node,
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID ||
targetTypeId == ANYCOMPATIBLEARRAYOID ||
targetTypeId == ANYCOMPATIBLERANGEOID)
{
@@ -195,12 +196,12 @@ coerce_type(ParseState *pstate, Node *node,
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call the pseudotype's input
- * function, which will produce an error. Also, if what we have is a
- * domain over array, enum, or range, we have to relabel it to its
- * base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * the pseudotype's input function, which will produce an error. Also,
+ * if what we have is a domain over array, enum, range, or multirange,
+ * we have to relabel it to its base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1531,8 +1532,8 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* 1) All arguments declared ANYELEMENT must have the same datatype.
* 2) All arguments declared ANYARRAY must have the same datatype,
* which must be a varlena array type.
- * 3) All arguments declared ANYRANGE must have the same datatype,
- * which must be a range type.
+ * 3) All arguments declared ANYRANGE or ANYMULTIRANGE must be a range or
+ * multirange type, all derived from the same base datatype.
* 4) If there are arguments of more than one of these polymorphic types,
* the array element type and/or range subtype must be the same as each
* other and the same as the ANYELEMENT type.
@@ -1582,8 +1583,10 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid anycompatible_range_typeid = InvalidOid;
Oid anycompatible_range_typelem = InvalidOid;
+ Oid range_typelem = InvalidOid;
bool have_anynonarray = false;
bool have_anyenum = false;
bool have_anycompatible_nonarray = false;
@@ -1632,6 +1635,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
else if (decl_type == ANYCOMPATIBLEOID ||
decl_type == ANYCOMPATIBLENONARRAYOID)
{
@@ -1722,8 +1734,6 @@ check_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- Oid range_typelem;
-
range_typelem = get_range_subtype(range_typeid);
if (!OidIsValid(range_typelem))
return false; /* should be a range, but isn't */
@@ -1742,6 +1752,45 @@ check_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ Oid multirange_typelem;
+
+ multirange_typelem = get_range_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+ }
+
if (have_anynonarray)
{
/* require the element type to not be an array or domain over array */
@@ -1820,16 +1869,23 @@ check_generic_type_consistency(const Oid *actual_arg_types,
* 2) If return type is ANYARRAY, and any argument is ANYARRAY, use the
* argument's actual type as the function's return type.
* 3) Similarly, if return type is ANYRANGE, and any argument is ANYRANGE,
- * use the argument's actual type as the function's return type.
- * 4) Otherwise, if return type is ANYELEMENT or ANYARRAY, there should be
- * at least one ANYELEMENT, ANYARRAY, or ANYRANGE input; deduce the
- * return type from those inputs, or throw error if we can't.
- * 5) Otherwise, if return type is ANYRANGE, throw error. (There should
- * be at least one ANYRANGE input, since CREATE FUNCTION enforces that.)
- * 6) ANYENUM is treated the same as ANYELEMENT except that if it is used
+ * use the argument's actual type as the function's return type. Or
+ * if any argument is ANYMULTIRANGE, use its range type as the function's
+ * return type.
+ * 4) Otherwise, if return type is ANYMULTIRANGE, and any argument is
+ * ANYMULTIRANGE, use the argument's actual type as the function's return
+ * type. Or if any argument is ANYRANGE, use its multirange type as the
+ * function's return type.
+ * 5) Otherwise, if return type is ANYELEMENT or ANYARRAY, there should be
+ * at least one ANYELEMENT, ANYARRAY, ANYRANGE, or ANYMULTIRANGE input;
+ * deduce the return type from those inputs, or throw error if we can't.
+ * 6) Otherwise, if return type is ANYRANGE, throw error. (There should
+ * be at least one ANYRANGE or ANYMULTIRANGE input, since CREATE FUNCTION
+ * enforces that.)
+ * 7) ANYENUM is treated the same as ANYELEMENT except that if it is used
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
- * 7) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
+ * 8) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
* we add the extra condition that the ANYELEMENT type must not be an array.
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
* is an extra restriction if not.)
@@ -1882,10 +1938,13 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid anycompatible_typeid = InvalidOid;
Oid anycompatible_array_typeid = InvalidOid;
Oid anycompatible_range_typeid = InvalidOid;
Oid anycompatible_range_typelem = InvalidOid;
+ Oid range_typelem;
+ Oid multirange_typelem;
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
bool have_anycompatible_nonarray = (rettype == ANYCOMPATIBLENONARRAYOID);
@@ -1970,6 +2029,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ n_poly_args++;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_poly_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
else if (decl_type == ANYCOMPATIBLEOID ||
decl_type == ANYCOMPATIBLENONARRAYOID)
{
@@ -2109,8 +2188,6 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- Oid range_typelem;
-
range_typelem = get_range_subtype(range_typeid);
if (!OidIsValid(range_typelem))
ereport(ERROR,
@@ -2139,6 +2216,61 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(elem_typeid))));
}
}
+ else
+ range_typelem = InvalidOid;
+
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_range_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * if we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
+ }
+ else
+ multirange_typelem = InvalidOid;
if (!OidIsValid(elem_typeid))
{
@@ -2147,6 +2279,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -2327,6 +2460,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -2363,6 +2507,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYCOMPATIBLE use the appropriate type */
if (rettype == ANYCOMPATIBLEOID ||
rettype == ANYCOMPATIBLENONARRAYOID)
@@ -2414,20 +2574,36 @@ check_valid_polymorphic_signature(Oid ret_type,
const Oid *declared_arg_types,
int nargs)
{
- if (ret_type == ANYRANGEOID || ret_type == ANYCOMPATIBLERANGEOID)
+ if (ret_type == ANYRANGEOID || ret_type == ANYMULTIRANGEOID)
+ {
+ /*
+ * ANYRANGE and ANYMULTIRANGE require an ANYRANGE or ANYMULTIRANGE input,
+ * else we can't tell which of several range types with the same element
+ * type to use.
+ */
+ for (int i = 0; i < nargs; i++)
+ {
+ if (declared_arg_types[i] == ANYRANGEOID ||
+ declared_arg_types[i] == ANYMULTIRANGEOID)
+ return NULL; /* OK */
+ }
+ return psprintf(_("A result of type %s requires at least one input of type anyrange or anymultirange."),
+ format_type_be(ret_type));
+ }
+ else if (ret_type == ANYCOMPATIBLERANGEOID)
{
/*
- * ANYRANGE requires an ANYRANGE input, else we can't tell which of
- * several range types with the same element type to use. Likewise
- * for ANYCOMPATIBLERANGE.
+ * ANYCOMPATIBLERANGE requires an ANYCOMPATIBLERANGE input,
+ * else we can't tell which of several range types with the same element
+ * type to use.
*/
for (int i = 0; i < nargs; i++)
{
if (declared_arg_types[i] == ret_type)
return NULL; /* OK */
}
- return psprintf(_("A result of type %s requires at least one input of type %s."),
- format_type_be(ret_type), format_type_be(ret_type));
+ return psprintf(_("A result of type %s requires at least one input of type anycompatiblerange."),
+ format_type_be(ret_type));
}
else if (IsPolymorphicTypeFamily1(ret_type))
{
@@ -2438,7 +2614,7 @@ check_valid_polymorphic_signature(Oid ret_type,
return NULL; /* OK */
}
/* Keep this list in sync with IsPolymorphicTypeFamily1! */
- return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange."),
+ return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange."),
format_type_be(ret_type));
}
else if (IsPolymorphicTypeFamily2(ret_type))
@@ -2590,6 +2766,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..7118dd0034 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..bd3cf3dc93
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,2186 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ FmgrInfo typioproc; /* range type's I/O proc */
+ Oid typioparam; /* range type's I/O parameter */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData *get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks either either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str = NULL;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */ ;
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **)
+ repalloc(ranges, range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(InputFunctionCall(&cache->typioproc,
+ range_str_copy,
+ cache->typioparam,
+ typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->typioproc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(ReceiveFunctionCall(&cache->typioproc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range;
+
+ range = RangeTypePGetDatum(ranges[i]);
+ range = PointerGetDatum(SendFunctionCall(&cache->typioproc, range));
+
+ pq_sendint32(buf, VARSIZE(range) - VARHDRSZ);
+ pq_sendbytes(buf, VARDATA(range), VARSIZE(range) - VARHDRSZ);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ Oid typiofunc;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &typiofunc);
+
+ if (!OidIsValid(typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(typiofunc, &cache->typioproc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ bytelen += MAXALIGN(VARSIZE(ranges[i]));
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ memcpy(ptr, ranges[i], VARSIZE(ranges[i]));
+ ptr += MAXALIGN(VARSIZE(ranges[i]));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of arbitrary ranges into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ *
+ * Returns the number of slots actually used, which may be less than
+ * input_range_count but never more.
+ *
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType *multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor2(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_CARDINALITY_VIOLATION),
+ errmsg("multiranges cannot be constructed from multi-dimensional arrays")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Construct multirange value from a single range.
+ * It'd be nice if we could just use multirange_constructor2
+ * for this case, but we need a non-variadic single-arg function
+ * to let us define a CAST from a range to its multirange.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ RangeType *range;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ range = PG_GETARG_RANGE_P(0);
+
+ /* Make sure the range type matches. */
+ rngtypid = RangeTypeGetOid(range);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 1, &range));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ elog(ERROR, /* can't happen */
+ "niladic multirange constructor must not receive arguments");
+}
+
+
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+multirange_union(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
+}
+
+/* multirange minus */
+Datum
+multirange_minus(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+multirange_intersect(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+ /*-----------------------------------------------
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1,
+ * but one extra won't hurt.
+ *-----------------------------------------------
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType *mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges1[range_count1 - 1],
+ ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype,
+ ranges1[0],
+ ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1, MultirangeType *mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
+ return false;
+
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
+ return false;
+ }
+
+ /* All ranges in mr2 are satisfied */
+ return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 18f2ee8226..248ea7f44a 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -52,6 +52,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
}
Datum
+binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
+binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_array_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
binary_upgrade_set_next_toast_pg_type_oid(PG_FUNCTION_ARGS)
{
Oid typoid = PG_GETARG_OID(0);
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 3d6b2f9093..4fcd8ce6de 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -228,6 +229,30 @@ anycompatiblerange_out(PG_FUNCTION_ARGS)
}
/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
+/*
* void
*
* We support void_in so that PL functions can return VOID without any
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 01ad8bc240..0af3c67296 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -431,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -452,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -475,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -485,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -495,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -505,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -515,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -957,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -969,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -993,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1101,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1110,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1132,9 +1180,81 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
+}
+
+/* range, range -> range, range functions */
+
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
+ {
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
+ }
+
+ return false;
}
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
+}
+
+
/* Btree support */
/* btree comparator */
@@ -1144,12 +1264,6 @@ range_cmp(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
- RangeBound lower1,
- lower2;
- RangeBound upper1,
- upper2;
- bool empty1,
- empty2;
int cmp;
check_stack_depth(); /* recurses when subtype is a range type */
@@ -1160,6 +1274,28 @@ range_cmp(PG_FUNCTION_ARGS)
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+ cmp = range_cmp_internal(typcache, r1, r2);
+
+ PG_FREE_IF_COPY(r1, 0);
+ PG_FREE_IF_COPY(r2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
@@ -1177,10 +1313,7 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
- PG_FREE_IF_COPY(r1, 0);
- PG_FREE_IF_COPY(r2, 1);
-
- PG_RETURN_INT32(cmp);
+ return cmp;
}
/* inequality operators using the range_cmp function */
@@ -1218,13 +1351,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1363,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1404,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1433,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1471,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1770,6 +1917,21 @@ range_get_flags(const RangeType *range)
}
/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
+/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
* This is only needed in GiST operations, so we don't include a provision
@@ -1938,6 +2100,46 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
}
/*
+ * qsort callback for sorting ranges.
+ *
+ * Two empty ranges compare equal; an empty range sorts to the left of any
+ * non-empty range. Two non-empty ranges are sorted by lower bound first
+ * and by upper bound next.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
+/*
* Build an empty range value of the type indicated by the typcache entry.
*/
RangeType *
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 27bbb58f56..c6c079c623 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2495,6 +2495,16 @@ type_is_range(Oid typid)
}
/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
+/*
* get_type_category_preferred
*
* Given the type OID, fetch its category and preferred-type status.
@@ -3149,7 +3159,7 @@ get_namespace_name_or_temp(Oid nspid)
return get_namespace_name(nspid);
}
-/* ---------- PG_RANGE CACHE ---------- */
+/* ---------- PG_RANGE CACHES ---------- */
/*
* get_range_subtype
@@ -3202,6 +3212,56 @@ get_range_collation(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngmultitypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_range_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_range_multirange_subtype(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGEMULTIRANGE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 53d9ddf159..7654331a59 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -651,6 +651,18 @@ static const struct cachedesc cacheinfo[] = {
},
64
},
+ {RangeRelationId, /* RANGEMULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngmultitypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+
{RangeRelationId, /* RANGETYPE */
RangeTypidIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 854f133f9b..160846cf10 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -286,6 +286,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -302,6 +303,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -553,8 +557,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -591,7 +595,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -616,7 +620,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -641,7 +645,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -693,6 +697,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -737,6 +748,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -817,6 +835,16 @@ lookup_type_cache(Oid type_id, int flags)
}
/*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
+ /*
* If requested, get information about a domain type
*/
if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
@@ -927,6 +955,22 @@ load_rangetype_info(TypeCacheEntry *typentry)
typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
}
+/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Oid rangetypeOid;
+
+ rangetypeOid = get_range_multirange_subtype(typentry->type_id);
+ if (!OidIsValid(rangetypeOid))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
/*
* load_domaintype_info --- helper routine to set up domain constraint info
@@ -1527,11 +1571,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1574,6 +1618,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 78ed857203..ab24359981 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -35,6 +35,7 @@ typedef struct polymorphic_actuals
Oid anyelement_type; /* anyelement mapping, if known */
Oid anyarray_type; /* anyarray mapping, if known */
Oid anyrange_type; /* anyrange mapping, if known */
+ Oid anymultirange_type; /* anymultirange mapping, if known */
} polymorphic_actuals;
static void shutdown_MultiFuncCall(Datum arg);
@@ -46,6 +47,7 @@ static TypeFuncClass internal_get_result_type(Oid funcid,
static void resolve_anyelement_from_others(polymorphic_actuals *actuals);
static void resolve_anyarray_from_others(polymorphic_actuals *actuals);
static void resolve_anyrange_from_others(polymorphic_actuals *actuals);
+static void resolve_anymultirange_from_others(polymorphic_actuals *actuals);
static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
oidvector *declared_args,
Node *call_expr);
@@ -503,6 +505,31 @@ resolve_anyelement_from_others(polymorphic_actuals *actuals)
format_type_be(range_base_type))));
actuals->anyelement_type = range_typelem;
}
+ else if (OidIsValid(actuals->anymultirange_type))
+ {
+ /* Use the element type based on the multirange type */
+ Oid multirange_base_type = getBaseType(actuals->anymultirange_type);
+ Oid multirange_typelem =
+ get_range_multirange_subtype(multirange_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_base_type))));
+
+ Oid range_base_type = getBaseType(multirange_typelem);
+ Oid range_typelem = get_range_subtype(range_base_type);
+
+ if (!OidIsValid(range_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s does not contain a range type but type %s",
+ "anymultirange",
+ format_type_be(range_base_type))));
+ actuals->anyelement_type = range_typelem;
+ }
else
elog(ERROR, "could not determine polymorphic type");
}
@@ -540,10 +567,54 @@ static void
resolve_anyrange_from_others(polymorphic_actuals *actuals)
{
/*
- * We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types with the same subtype.
+ * We can't deduce a range type from other polymorphic array or base types,
+ * because there may be multiple range types with the same subtype, but we
+ * can deduce it from a polymorphic multirange type.
*/
- elog(ERROR, "could not determine polymorphic type");
+ if (OidIsValid(actuals->anymultirange_type))
+ {
+ /* Use the element type based on the multirange type */
+ Oid multirange_base_type = getBaseType(actuals->anymultirange_type);
+ Oid multirange_typelem =
+ get_range_multirange_subtype(multirange_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_base_type))));
+ actuals->anyrange_type = multirange_typelem;
+ }
+ else
+ elog(ERROR, "could not determine polymorphic type");
+}
+
+/*
+ * Resolve actual type of ANYMULTIRANGE from other polymorphic inputs
+ */
+static void
+resolve_anymultirange_from_others(polymorphic_actuals *actuals)
+{
+ /*
+ * We can't deduce a multirange type from polymorphic array or base types,
+ * because there may be multiple range types with the same subtype, but we
+ * can deduce it from a polymorphic range type.
+ */
+ if (OidIsValid(actuals->anyrange_type))
+ {
+ Oid range_base_type = getBaseType(actuals->anyrange_type);
+ Oid multirange_typeid = get_range_multirange(range_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(actuals->anyrange_type))));
+ actuals->anymultirange_type = multirange_typeid;
+ }
+ else
+ elog(ERROR, "could not determine polymorphic type");
}
/*
@@ -566,6 +637,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anycompatible_result = false;
bool have_anycompatible_array_result = false;
bool have_anycompatible_range_result = false;
@@ -594,6 +666,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
have_polymorphic_result = true;
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_polymorphic_result = true;
+ have_anymultirange_result = true;
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
have_polymorphic_result = true;
@@ -660,6 +736,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
}
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ {
+ poly_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ return false;
+ }
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
if (!OidIsValid(anyc_actuals.anyelement_type))
@@ -703,6 +788,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&poly_actuals);
+
if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
resolve_anyelement_from_others(&anyc_actuals);
@@ -780,6 +868,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ poly_actuals.anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
TupleDescInitEntry(tupdesc, i + 1,
@@ -834,6 +930,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anycompatible_result = false;
bool have_anycompatible_array_result = false;
bool have_anycompatible_range_result = false;
@@ -912,6 +1009,24 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = poly_actuals.anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
+ have_anymultirange_result = true;
+ }
+ else
+ {
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ {
+ poly_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ return false;
+ }
+ argtypes[i] = poly_actuals.anymultirange_type;
+ }
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
@@ -988,6 +1103,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&poly_actuals);
+
if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
resolve_anyelement_from_others(&anyc_actuals);
@@ -1013,6 +1131,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = poly_actuals.anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = poly_actuals.anymultirange_type;
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
argtypes[i] = anyc_actuals.anyelement_type;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index ced0681ec3..f8567ad9a2 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -271,7 +271,8 @@ static void dumpSearchPath(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type);
+ bool force_array_type,
+ bool include_multirange_type);
static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
static void binary_upgrade_set_pg_class_oids(Archive *fout,
@@ -1580,7 +1581,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4340,16 +4341,49 @@ append_depends_on_extension(Archive *fout,
}
}
+static Oid
+get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+{
+ /*
+ * If the old version didn't assign an array type, but the new version
+ * does, we must select an unused type OID to assign. This currently only
+ * happens for domains, when upgrading pre-v11 to v11 and up.
+ *
+ * Note: local state here is kind of ugly, but we must have some, since we
+ * mustn't choose the same unused OID more than once.
+ */
+ static Oid next_possible_free_oid = FirstNormalObjectId;
+ PGresult *res;
+ bool is_dup;
+
+ do
+ {
+ ++next_possible_free_oid;
+ printfPQExpBuffer(upgrade_query,
+ "SELECT EXISTS(SELECT 1 "
+ "FROM pg_catalog.pg_type "
+ "WHERE oid = '%u'::pg_catalog.oid);",
+ next_possible_free_oid);
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+ is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
+ PQclear(res);
+ } while (is_dup);
+
+ return next_possible_free_oid;
+}
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type)
+ bool force_array_type,
+ bool include_multirange_type)
{
PQExpBuffer upgrade_query = createPQExpBuffer();
PGresult *res;
Oid pg_type_array_oid;
+ Oid pg_type_multirange_oid;
+ Oid pg_type_multirange_array_oid;
appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
@@ -4370,33 +4404,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQclear(res);
if (!OidIsValid(pg_type_array_oid) && force_array_type)
- {
- /*
- * If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
- *
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
- */
- static Oid next_possible_free_oid = FirstNormalObjectId;
- bool is_dup;
-
- do
- {
- ++next_possible_free_oid;
- printfPQExpBuffer(upgrade_query,
- "SELECT EXISTS(SELECT 1 "
- "FROM pg_catalog.pg_type "
- "WHERE oid = '%u'::pg_catalog.oid);",
- next_possible_free_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
- is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
- PQclear(res);
- } while (is_dup);
-
- pg_type_array_oid = next_possible_free_oid;
- }
+ pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
if (OidIsValid(pg_type_array_oid))
{
@@ -4407,6 +4415,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
pg_type_array_oid);
}
+ /*
+ * Pre-set the multirange type oid and its own array type oid.
+ */
+ if (include_multirange_type)
+ {
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBuffer(upgrade_query,
+ "SELECT t.oid, t.typarray "
+ "FROM pg_catalog.pg_type t "
+ "JOIN pg_catalog.pg_range r "
+ "ON t.oid = r.rngmultitypid "
+ "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
+ pg_type_oid);
+
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+
+ pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+ pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
+
+ PQclear(res);
+ }
+ else
+ {
+ pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ }
+
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_oid);
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_array_oid);
+ }
+
destroyPQExpBuffer(upgrade_query);
}
@@ -4440,7 +4488,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
- pg_type_oid, false);
+ pg_type_oid, false, false);
if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
{
@@ -4989,6 +5037,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
@@ -8103,9 +8156,12 @@ getProcLangs(Archive *fout, int *numProcLangs)
/*
* getCasts
- * get basic information about every cast in the system
+ * get basic information about most casts in the system
*
* numCasts is set to the number of casts read in
+ *
+ * Skip casts from a range to its multirange, since we'll create those
+ * automatically.
*/
CastInfo *
getCasts(Archive *fout, int *numCasts)
@@ -8123,7 +8179,20 @@ getCasts(Archive *fout, int *numCasts)
int i_castcontext;
int i_castmethod;
- if (fout->remoteVersion >= 80400)
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBufferStr(query, "SELECT tableoid, oid, "
+ "castsource, casttarget, castfunc, castcontext, "
+ "castmethod "
+ "FROM pg_cast c "
+ "WHERE NOT EXISTS ( "
+ "SELECT 1 FROM pg_range r "
+ "WHERE c.castsource = r.rngtypid "
+ "AND c.casttarget = r.rngmultitypid "
+ ") "
+ "ORDER BY 3,4");
+ }
+ else if (fout->remoteVersion >= 80400)
{
appendPQExpBufferStr(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
@@ -10311,7 +10380,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
qualtypname);
@@ -10437,7 +10506,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, true);
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
qualtypname);
@@ -10543,7 +10612,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
qualtypname);
@@ -10748,7 +10817,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
@@ -10935,7 +11004,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- true); /* force array type */
+ true, /* force array type */
+ false); /* force multirange type */
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -11123,7 +11193,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
{
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
}
@@ -11397,7 +11467,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
stinfo->baseType->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
fmtQualifiedDumpable(stinfo));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index e0c6444ef6..90a93b4cb2 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -176,6 +176,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 70157cf90a..c262265b95 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -139,8 +139,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 12d94fe1b3..01d95117cd 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,6 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 8be303870f..566f9364c5 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -327,6 +327,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_rngmultitypid_index, 8001, on pg_range using btree(rngmultitypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index ffabe275c0..03bab74253 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..827ac4f03c 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 75c0152b66..cf0cac5641 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -285,6 +285,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
@@ -445,6 +447,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
@@ -1263,12 +1270,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index 01c5328ddd..f555af90ac 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -526,4 +526,17 @@
{ castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
castcontext => 'e', castmethod => 'f' },
+# range to multirange
+{ castsource => 'int4range', casttarget => 'int4multirange', castfunc => 'int4multirange(int4range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'int8range', casttarget => 'int8multirange', castfunc => 'int8multirange(int8range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'numrange', casttarget => 'nummultirange', castfunc => 'nummultirange(numrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'daterange', casttarget => 'datemultirange', castfunc => 'datemultirange(daterange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tsrange', casttarget => 'tsmultirange', castfunc => 'tsmultirange(tsrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tstzrange', casttarget => 'tstzmultirange', castfunc => 'tstzmultirange(tstzrange)',
+ castcontext => 'e', castmethod => 'f' },
]
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index ab2f50c9eb..eebebbcdb4 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 7c135da3b1..24f4a32203 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3297,5 +3297,174 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'contsel', oprjoin => 'contjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 26227df216..901fd7b303 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 87d25d4a4b..b3b208ca3f 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9695,6 +9695,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9744,6 +9748,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9812,6 +9823,258 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8114',
+ proname => 'multirange_union', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union' },
+{ oid => '8120',
+ proname => 'multirange_minus', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus' },
+{ oid => '8126',
+ proname => 'multirange_intersect', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8146', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 't',
+ prorettype => 'int4multirange', proargtypes => 'int4range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8147', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 't',
+ prorettype => 'nummultirange', proargtypes => 'numrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8148', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 't',
+ prorettype => 'tsmultirange', proargtypes => 'tsrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8149', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 't',
+ prorettype => 'tstzmultirange', proargtypes => 'tstzrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8150', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 't',
+ prorettype => 'datemultirange', proargtypes => 'daterange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8151', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 't',
+ prorettype => 'int8multirange', proargtypes => 'int8range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8152', descr => 'anymultirange cast',
+ proname => 'multirange', proisstrict => 't',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
@@ -10194,6 +10457,14 @@
proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
+{ oid => '8144', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
+{ oid => '8145', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
{ oid => '3585', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index 479754c245..10060255c9 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ rngmultitypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', rngmultitypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', rngmultitypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', rngmultitypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ rngmultitypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ rngmultitypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index 98172bb1b6..60a8fde518 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -34,6 +34,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* OID of range's element type (subtype) */
Oid rngsubtype BKI_LOOKUP(pg_type);
+ /* OID of the range's multirange type */
+ Oid rngmultitypid BKI_LOOKUP(pg_type);
+
/* collation for this range type, or 0 */
Oid rngcollation BKI_DEFAULT(0);
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 2e6110e3f2..cb3d159bf1 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -491,6 +491,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -620,5 +654,10 @@
typtype => 'p', typcategory => 'P', typinput => 'anycompatiblerange_in',
typoutput => 'anycompatiblerange_out', typreceive => '-', typsend => '-',
typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 7b37562648..f1818d9a52 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -263,6 +263,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -304,7 +305,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#define IsPolymorphicTypeFamily2(typid) \
((typid) == ANYCOMPATIBLEOID || \
@@ -369,4 +371,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 0162bc2ffe..80e3a5e399 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
+extern Oid AssignTypeMultirangeOid(void);
+extern Oid AssignTypeMultirangeArrayOid(void);
extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 4e646c55e9..80410dcd04 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -155,6 +155,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -181,6 +182,8 @@ extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
extern Oid get_range_collation(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_range_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
extern bool get_index_isreplident(Oid index_oid);
extern bool get_index_isvalid(Oid index_oid);
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..469444776c
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern MultirangeType *multirange_minus_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType *multirange_intersect_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType *range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType *make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType *make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 0cbbf09033..204aee4054 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
@@ -92,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -115,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -125,6 +141,7 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
@@ -132,8 +149,12 @@ extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f27b73d76d..e8f393520a 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -79,6 +79,7 @@ enum SysCacheIdentifier
PUBLICATIONOID,
PUBLICATIONREL,
PUBLICATIONRELMAP,
+ RANGEMULTIRANGE,
RANGETYPE,
RELNAMENSP,
RELOID,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index cdd20e56d7..33f332a141 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -101,6 +101,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
/*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype; /* multirange's range underlying type */
+
+ /*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
*/
@@ -143,6 +148,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 828ff5a288..1e3ff02289 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -513,6 +513,8 @@ do_compile(FunctionCallInfo fcinfo,
else if (rettypeid == ANYRANGEOID ||
rettypeid == ANYCOMPATIBLERANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY or ANYCOMPATIBLE */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2089,6 +2091,7 @@ build_datatype(HeapTuple typeTup, int32 typmod,
case TYPTYPE_BASE:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
+ case TYPTYPE_MULTIRANGE:
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_COMPOSITE:
@@ -2507,6 +2510,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYCOMPATIBLERANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..8232795148 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -140,6 +140,7 @@ owner of sequence deptest_a_seq
owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
+owner of type deptest_multirange
owner of type deptest_range
owner of table deptest2
owner of sequence ss1
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..23a06b301f
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,2395 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+ int4multirange
+----------------
+ {}
+(1 row)
+
+select int4range(1, 3)::int4multirange;
+ int4range
+-----------
+ {[1,3)}
+(1 row)
+
+select int4range(1, null)::int4multirange;
+ int4range
+-----------
+ {[1,)}
+(1 row)
+
+select int4range(null, null)::int4multirange;
+ int4range
+-----------
+ {(,)}
+(1 row)
+
+select 'empty'::textrange::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textrange('a', 'c')::textmultirange;
+ textrange
+-----------
+ {[a,c)}
+(1 row)
+
+select textrange('a', null)::textmultirange;
+ textrange
+-----------
+ {[a,)}
+(1 row)
+
+select textrange(null, null)::textmultirange;
+ textrange
+-----------
+ {(,)}
+(1 row)
+
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT nummultirange() + nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT nummultirange() - nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() * nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+ mr_polymorphic
+----------------
+ {[1,4)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 3c0b21d633..4ec99ea800 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -206,6 +213,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -224,6 +233,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -335,7 +346,8 @@ WHERE p1.prorettype IN
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+----------------
@@ -350,20 +362,25 @@ ORDER BY 2;
3532 | enum_recv
(9 rows)
--- anyrange is tighter than the rest, can only resolve from anyrange input
+-- anyrange and anymultirange are tighter than the rest, can only resolve
+-- from each other
SELECT p1.oid, p1.proname
FROM pg_proc as p1
-WHERE p1.prorettype = 'anyrange'::regtype
+WHERE p1.prorettype IN ('anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
- 'anyrange'::regtype = ANY (p1.proargtypes)
+ ('anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
+ 8002 | anymultirange_in
3832 | anyrange_in
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(4 rows)
+(7 rows)
-- similarly for the anycompatible family
SELECT p1.oid, p1.proname
@@ -2181,13 +2198,14 @@ ORDER BY 1, 2, 3;
| float_ops | float4_ops | real
| float_ops | float8_ops | double precision
| jsonb_ops | jsonb_ops | jsonb
+ | multirange_ops | multirange_ops | anymultirange
| numeric_ops | numeric_ops | numeric
| range_ops | range_ops | anyrange
| record_image_ops | record_image_ops | record
| record_ops | record_ops | record
| tsquery_ops | tsquery_ops | tsquery
| tsvector_ops | tsvector_ops | tsvector
-(14 rows)
+(15 rows)
-- **************** pg_index ****************
-- Look for illegal values in pg_index fields.
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index d0a6b630b8..65827c03e6 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -1811,7 +1811,7 @@ begin
return array[x + 1, x + 2];
end$$ language plpgsql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function f1(x anyrange) returns anyarray as $$
begin
return array[lower(x), upper(x)];
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index d86a7004a6..e087db3ea6 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -61,7 +61,7 @@ create function polyf(x anyelement) returns anyrange as $$
select array[x + 1, x + 2]
$$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function polyf(x anyrange) returns anyarray as $$
select array[lower(x), upper(x)]
$$ language sql;
@@ -231,7 +231,7 @@ CREATE AGGREGATE myaggp01a(*) (SFUNC = stfnp, STYPE = int4[],
CREATE AGGREGATE myaggp02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- N P
-- should CREATE
CREATE AGGREGATE myaggp03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -243,11 +243,11 @@ CREATE AGGREGATE myaggp03b(*) (SFUNC = stfp, STYPE = int4[],
CREATE AGGREGATE myaggp04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp04b(*) (SFUNC = stfp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- Case2 (R = P) && ((B = P) || (B = N))
-- -------------------------------------
-- S tf1 B tf2
@@ -302,13 +302,13 @@ ERROR: function tfp(integer[], anyelement) does not exist
CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N N P
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N P N
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -324,21 +324,21 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P N P
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P P N
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p,
@@ -368,11 +368,11 @@ CREATE AGGREGATE myaggn01b(*) (SFUNC = stfnp, STYPE = int4[],
CREATE AGGREGATE myaggn02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn02b(*) (SFUNC = stfnp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- N P
-- should CREATE
CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -382,7 +382,7 @@ CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
CREATE AGGREGATE myaggn04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- Case4 (R = N) && ((B = P) || (B = N))
-- -------------------------------------
-- S tf1 B tf2
@@ -436,21 +436,21 @@ ERROR: function tfp(integer[], anyelement) does not exist
CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N N P
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N P N
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -472,13 +472,13 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P N P
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P P N
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p,
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 7eced28452..71e2769f44 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -1556,7 +1556,7 @@ DROP FUNCTION dup(anyelement);
CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE FUNCTION dup (f1 anycompatible, f2 anycompatiblearray, f3 out anycompatible, f4 out anycompatiblearray)
AS 'select $1, $2' LANGUAGE sql;
SELECT dup(22, array[44]);
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index c421f5394f..763e3819a5 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1371,12 +1389,12 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function range_add_bounds(anyrange)
returns anyelement as 'select lower($1) + upper($1)' language sql;
select range_add_bounds(int4range(1, 17));
@@ -1508,6 +1526,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1525,6 +1544,16 @@ select * from outparam2_succeed(int4range(1,11));
{1,11} | {11,1}
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1547,14 +1576,14 @@ select * from table_succeed(int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 192445878d..d9ce961be2 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
numrange_test|t
onek|t
onek2|t
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 274130e706..8df1ae47e4 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -195,18 +195,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -240,17 +241,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -318,18 +320,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -363,17 +366,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -631,3 +635,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
+ rngtypid | rngsubtype | rngmultitypid
+----------+------------+---------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index d2b17dd3ea..5355da7b01 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index acba391332..1f677f63af 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..0f70919e1a
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,630 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+select int4range(1, 3)::int4multirange;
+select int4range(1, null)::int4multirange;
+select int4range(null, null)::int4multirange;
+select 'empty'::textrange::textmultirange;
+select textrange('a', 'c')::textmultirange;
+select textrange('a', null)::textmultirange;
+select textrange(null, null)::textmultirange;
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT nummultirange() + nummultirange();
+SELECT nummultirange() + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT nummultirange() - nummultirange();
+SELECT nummultirange() - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+SELECT nummultirange() * nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 389d5b2464..f1e1bfb7d6 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -279,16 +290,19 @@ WHERE p1.prorettype IN
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
--- anyrange is tighter than the rest, can only resolve from anyrange input
+-- anyrange and anymultirange are tighter than the rest, can only resolve
+-- from each other
SELECT p1.oid, p1.proname
FROM pg_proc as p1
-WHERE p1.prorettype = 'anyrange'::regtype
+WHERE p1.prorettype IN ('anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
- 'anyrange'::regtype = ANY (p1.proargtypes)
+ ('anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- similarly for the anycompatible family
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 4048b1d185..0276d19136 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -528,6 +532,7 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
@@ -539,6 +544,13 @@ create function outparam2_succeed(r anyrange, out lu anyarray, out ul anyarray)
select * from outparam2_succeed(int4range(1,11));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index 4b492ce062..07879d9a57 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -153,7 +153,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -182,7 +182,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -234,7 +234,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -263,7 +263,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -467,3 +467,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index fcfcf56f4f..34a06fe20f 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1356,6 +1356,9 @@ MultiXactMember
MultiXactOffset
MultiXactStateData
MultiXactStatus
+MultirangeIOData
+MultirangeParseState
+MultirangeType
MyData
NDBOX
NODE
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-14 18:13 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-17 04:52 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-19 20:41 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-19 20:43 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-20 05:24 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-03-23 23:32 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-24 04:23 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 1 reply; 152+ messages in thread
From: Alvaro Herrera @ 2020-03-23 23:32 UTC (permalink / raw)
To: Paul A Jungwirth <pj@illuminatedcomputing.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
On 2020-Mar-19, Paul A Jungwirth wrote:
> On Thu, Mar 19, 2020 at 1:43 PM Paul A Jungwirth
> <pj@illuminatedcomputing.com> wrote:
> > On Thu, Mar 19, 2020 at 1:42 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
> > > There's been another flurry of commits in the polymorphic types area.
> > > Can you please rebase again?
> >
> > I noticed that too. :-) I'm about halfway through a rebase right now.
> > I can probably finish it up tonight.
>
> Here is that patch. I should probably add an anycompatiblemultirange
> type now too? I'll get started on that tomorrow.
Thanks for the new version. Here's a few minor adjustments while I
continue to read through it.
Thinking about the on-disk representation, can we do better than putting
the contained ranges in long-varlena format, including padding; also we
include the type OID with each element. Sounds wasteful. A more
compact representation might be to allow short varlenas and doing away
with the alignment padding, put the the type OID just once. This is
important because we cannot change it later.
I'm also wondering if multirange_in() is the right strategy. Would it
be sensible to give each range to range_parse or range_parse_bounde, so
that it determines where each range starts and ends? Then that function
doesn't have to worry about each quote and escape, duplicating range
parsing code. (This will probably require changing signature of the
rangetypes.c function, and exporting it; for example have
range_parse_bound allow bound_str to be NULL and in that case don't mess
with the StringInfo and just return the end position of the parsed
bound.)
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
[text/x-diff] 0001-Fix-typo.patch (985B, ../../20200323233227.GA5607@alvherre.pgsql/2-0001-Fix-typo.patch)
download | inline diff:
From bd8b814426bef6f5a620351557cdc953ba0ae22e Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 23 Mar 2020 18:50:02 -0300
Subject: [PATCH 1/5] Fix typo
---
src/backend/utils/adt/multirangetypes.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index bd3cf3dc93..6b3cde8eca 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -79,7 +79,7 @@ static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range
* to range_in, but we have to detect quoting and backslash-escaping
* which can happen for range bounds.
* Backslashes can escape something inside or outside a quoted string,
- * and a quoted string can escape quote marks either either backslashes
+ * and a quoted string can escape quote marks with either backslashes
* or double double-quotes.
*/
Datum
--
2.20.1
[text/x-diff] 0002-Remove-trailing-useless.patch (764B, ../../20200323233227.GA5607@alvherre.pgsql/3-0002-Remove-trailing-useless.patch)
download | inline diff:
From afd017faab3d6aff3ca8618d95a6f9c918ab8cca Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 23 Mar 2020 18:50:19 -0300
Subject: [PATCH 2/5] Remove trailing useless ;
---
src/backend/utils/adt/multirangetypes.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 6b3cde8eca..6e9cf77651 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -131,7 +131,7 @@ multirange_in(PG_FUNCTION_ARGS)
input_str),
errdetail("Unexpected end of input.")));
- /* skip whitespace */ ;
+ /* skip whitespace */
if (isspace((unsigned char) ch))
continue;
--
2.20.1
[text/x-diff] 0003-reduce-palloc-strlcpy-to-pnstrdup.patch (1022B, ../../20200323233227.GA5607@alvherre.pgsql/4-0003-reduce-palloc-strlcpy-to-pnstrdup.patch)
download | inline diff:
From d7c1ea171de018d025ab5de57f572a9353b02fdf Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 23 Mar 2020 18:50:40 -0300
Subject: [PATCH 3/5] reduce palloc+strlcpy to pnstrdup
---
src/backend/utils/adt/multirangetypes.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 6e9cf77651..5b57416cfd 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -167,9 +167,8 @@ multirange_in(PG_FUNCTION_ARGS)
parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
else if (ch == ']' || ch == ')')
{
- range_str_len = ptr - range_str + 2;
- range_str_copy = palloc0(range_str_len);
- strlcpy(range_str_copy, range_str, range_str_len);
+ range_str_len = ptr - range_str + 1;
+ range_str_copy = pnstrdup(range_str, range_str_len);
if (range_capacity == range_count)
{
range_capacity *= 2;
--
2.20.1
[text/x-diff] 0004-silence-compiler-warning.patch (1.6K, ../../20200323233227.GA5607@alvherre.pgsql/5-0004-silence-compiler-warning.patch)
download | inline diff:
From 7698f57d1f5ab870c89a84da7bea24bef241a458 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 23 Mar 2020 18:50:59 -0300
Subject: [PATCH 4/5] silence compiler warning
---
src/backend/utils/fmgr/funcapi.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index ab24359981..3bfefcf48a 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -508,10 +508,13 @@ resolve_anyelement_from_others(polymorphic_actuals *actuals)
else if (OidIsValid(actuals->anymultirange_type))
{
/* Use the element type based on the multirange type */
- Oid multirange_base_type = getBaseType(actuals->anymultirange_type);
- Oid multirange_typelem =
- get_range_multirange_subtype(multirange_base_type);
+ Oid multirange_base_type;
+ Oid multirange_typelem;
+ Oid range_base_type;
+ Oid range_typelem;
+ multirange_base_type = getBaseType(actuals->anymultirange_type);
+ multirange_typelem = get_range_multirange_subtype(multirange_base_type);
if (!OidIsValid(multirange_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -519,8 +522,8 @@ resolve_anyelement_from_others(polymorphic_actuals *actuals)
"anymultirange",
format_type_be(multirange_base_type))));
- Oid range_base_type = getBaseType(multirange_typelem);
- Oid range_typelem = get_range_subtype(range_base_type);
+ range_base_type = getBaseType(multirange_typelem);
+ range_typelem = get_range_subtype(range_base_type);
if (!OidIsValid(range_typelem))
ereport(ERROR,
--
2.20.1
[text/x-diff] 0005-rename-get_range_multirange_subtype-to-get_multirang.patch (4.3K, ../../20200323233227.GA5607@alvherre.pgsql/6-0005-rename-get_range_multirange_subtype-to-get_multirang.patch)
download | inline diff:
From b70eb4fcff3819a3671f21164319750a5a174083 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 23 Mar 2020 18:58:14 -0300
Subject: [PATCH 5/5] rename get_range_multirange_subtype to
get_multirange_range
---
src/backend/parser/parse_coerce.c | 4 ++--
src/backend/utils/cache/lsyscache.c | 8 ++++----
src/backend/utils/cache/typcache.c | 2 +-
src/backend/utils/fmgr/funcapi.c | 4 ++--
src/include/utils/lsyscache.h | 2 +-
5 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index fb8c3e5cb0..91f80a0656 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1757,7 +1757,7 @@ check_generic_type_consistency(const Oid *actual_arg_types,
{
Oid multirange_typelem;
- multirange_typelem = get_range_multirange_subtype(multirange_typeid);
+ multirange_typelem = get_multirange_range(multirange_typeid);
if (!OidIsValid(multirange_typelem))
return false; /* should be a multirange, but isn't */
@@ -2222,7 +2222,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the multirange type, if we have one */
if (OidIsValid(multirange_typeid))
{
- multirange_typelem = get_range_multirange_subtype(multirange_typeid);
+ multirange_typelem = get_multirange_range(multirange_typeid);
if (!OidIsValid(multirange_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index c6c079c623..8d773d40c4 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3238,13 +3238,13 @@ get_range_multirange(Oid rangeOid)
}
/*
- * get_range_multirange_subtype
- * Returns the subtype of a given multirange type
+ * get_multirange_range
+ * Returns the range type of a given multirange
*
- * Returns InvalidOid if the type is not a multirange type.
+ * Returns InvalidOid if the type is not a multirange.
*/
Oid
-get_range_multirange_subtype(Oid multirangeOid)
+get_multirange_range(Oid multirangeOid)
{
HeapTuple tp;
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 160846cf10..f1d3cb97d0 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -964,7 +964,7 @@ load_multirangetype_info(TypeCacheEntry *typentry)
{
Oid rangetypeOid;
- rangetypeOid = get_range_multirange_subtype(typentry->type_id);
+ rangetypeOid = get_multirange_range(typentry->type_id);
if (!OidIsValid(rangetypeOid))
elog(ERROR, "cache lookup failed for multirange type %u",
typentry->type_id);
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 3bfefcf48a..ce09d0d128 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -514,7 +514,7 @@ resolve_anyelement_from_others(polymorphic_actuals *actuals)
Oid range_typelem;
multirange_base_type = getBaseType(actuals->anymultirange_type);
- multirange_typelem = get_range_multirange_subtype(multirange_base_type);
+ multirange_typelem = get_multirange_range(multirange_base_type);
if (!OidIsValid(multirange_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -579,7 +579,7 @@ resolve_anyrange_from_others(polymorphic_actuals *actuals)
/* Use the element type based on the multirange type */
Oid multirange_base_type = getBaseType(actuals->anymultirange_type);
Oid multirange_typelem =
- get_range_multirange_subtype(multirange_base_type);
+ get_multirange_range(multirange_base_type);
if (!OidIsValid(multirange_typelem))
ereport(ERROR,
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 80410dcd04..e361a7943b 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -183,7 +183,7 @@ extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
extern Oid get_range_collation(Oid rangeOid);
extern Oid get_range_multirange(Oid rangeOid);
-extern Oid get_range_multirange_subtype(Oid multirangeOid);
+extern Oid get_multirange_range(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
extern bool get_index_isreplident(Oid index_oid);
extern bool get_index_isvalid(Oid index_oid);
--
2.20.1
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-14 18:13 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-17 04:52 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-19 20:41 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-19 20:43 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-20 05:24 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-23 23:32 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-03-24 04:23 ` Paul Jungwirth <pj@illuminatedcomputing.com>
2020-04-04 23:10 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 1 reply; 152+ messages in thread
From: Paul Jungwirth @ 2020-03-24 04:23 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
Thanks Alvaro!
On Mon, Mar 23, 2020 at 4:33 PM Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:
>
> Thinking about the on-disk representation, can we do better than putting
> the contained ranges in long-varlena format, including padding; also we
> include the type OID with each element. Sounds wasteful. A more
> compact representation might be to allow short varlenas and doing away
> with the alignment padding, put the the type OID just once. This is
> important because we cannot change it later.
Can you give me some guidance on this? I don't know how to make the
on-disk format different from the in-memory format. (And for the
in-memory format, I think it's important to have actual RangeTypes
inside the multirange.) Is there something in the documentation, or a
README in the repo, or even another type I can follow?
> I'm also wondering if multirange_in() is the right strategy. Would
it> be sensible to give each range to range_parse or range_parse_bounde, so
> that it determines where each range starts and ends? Then that function
> doesn't have to worry about each quote and escape, duplicating range
> parsing code. (This will probably require changing signature of the
> rangetypes.c function, and exporting it; for example have
> range_parse_bound allow bound_str to be NULL and in that case don't mess
> with the StringInfo and just return the end position of the parsed
> bound.)
Yeah, I really wanted to do it that way originally too. As you say it
would require passing back more information from the range-parsing code.
I can take a stab at making the necessary changes. I'm a bit more
confident now than I was then in changing the range code we have already.
Regards,
--
Paul ~{:-)
pj@illuminatedcomputing.com
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-14 18:13 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-17 04:52 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-19 20:41 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-19 20:43 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-20 05:24 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-23 23:32 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-24 04:23 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
@ 2020-04-04 23:10 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-04-05 22:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-04-11 03:44 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
0 siblings, 2 replies; 152+ messages in thread
From: Alvaro Herrera @ 2020-04-04 23:10 UTC (permalink / raw)
To: Paul Jungwirth <pj@illuminatedcomputing.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
On 2020-Mar-23, Paul Jungwirth wrote:
> On Mon, Mar 23, 2020 at 4:33 PM Alvaro Herrera <alvherre@2ndquadrant.com>
> wrote:
> >
> > Thinking about the on-disk representation, can we do better than putting
> > the contained ranges in long-varlena format, including padding; also we
> > include the type OID with each element. Sounds wasteful. A more
> > compact representation might be to allow short varlenas and doing away
> > with the alignment padding, put the the type OID just once. This is
> > important because we cannot change it later.
>
> Can you give me some guidance on this? I don't know how to make the on-disk
> format different from the in-memory format. (And for the in-memory format, I
> think it's important to have actual RangeTypes inside the multirange.) Is
> there something in the documentation, or a README in the repo, or even
> another type I can follow?
Sorry I didn't reply earlier, but I didn't know the answer then and I
still don't know the answer now.
Anyway, I rebased this to verify that the code hasn't broken, and it
hasn't -- the tests still pass. There was a minor conflict in
pg_operator.dat which I fixed.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
[text/x-diff] v16-multirange.patch (332.2K, ../../20200404231018.GA4570@alvherre.pgsql/2-v16-multirange.patch)
download | inline diff:
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 930aeb767c..febb02e80a 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -285,6 +285,14 @@
</entry>
</row>
+ <row>
+ <entry><type>anymultirange</type></entry>
+ <entry>Simple</entry>
+ <entry>Indicates that a function accepts any multirange data type
+ (see <xref linkend="rangetypes"/>)
+ </entry>
+ </row>
+
<row>
<entry><type>anycompatible</type></entry>
<entry>Common</entry>
@@ -343,17 +351,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type> or <type>anyarray</type>,
- the actual range type in the <type>anyrange</type> positions must be a
- range whose subtype is the same type appearing in
- the <type>anyelement</type> positions and the same as the element type
- of the <type>anyarray</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -362,6 +368,19 @@
be an enum type.
</para>
+ <para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type> or <type>anyarray</type>,
+ the actual range type in the <type>anyrange</type> positions must be a
+ range whose subtype is the same type appearing in
+ the <type>anyelement</type> positions and the same as the element type
+ of the <type>anyarray</type> positions.
+ If there are positions declared <type>anymultirange</type>,
+ their actual multirange type must contain ranges matching parameters declared
+ <type>anyrange</type> and base elements matching parameters declared
+ <type>anyelement</type> and <type>anyarray</type>.
+ </para>
+
<para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 4d88b45e72..b2168f3801 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -13609,7 +13609,7 @@ SELECT NULLIF(value, '(none)') ...
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14080,12 +14080,14 @@ NULL baz</literallayout>(3 rows)</entry>
<title>Range Functions and Operators</title>
<para>
- See <xref linkend="rangetypes"/> for an overview of range types.
+ See <xref linkend="rangetypes"/> for an overview of range and multirange types.
</para>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14103,134 +14105,215 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
- <entry><literal>f</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">f</literallayout></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
- <entry><literal>[5,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)
+'{[5,10)}'::nummultirange + '{[15,20)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,20)
+{[5,10), [15,20)}</literallayout></entry>
</row>
<row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
- <entry><literal>[10,15)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)
+'{[5,15)}'::int8multirange * '{[10,20)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[10,15)
+{[10,15)}</literallayout></entry>
</row>
<row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
- <entry><literal>[5,10)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)
+'{[5,20)}'::int8multirange - '{[10,15)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,10)
+{[5,10), [15,20)}</literallayout></entry>
</row>
</tbody>
@@ -14248,19 +14331,31 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. There are separate operators for union and difference that take
+ multirange parameters and return a multirange, and they do not fail even if
+ their arguments are disjoint. So if you need a union or difference operation
+ for ranges that may be disjoint, you can avoid errors by first casting your
+ ranges to multiranges.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14309,6 +14404,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower(numrange(1.1,2.2))</literal></entry>
<entry><literal>1.1</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14320,6 +14426,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper(numrange(1.1,2.2))</literal></entry>
<entry><literal>2.2</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14331,6 +14448,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>isempty(numrange(1.1,2.2))</literal></entry>
<entry><literal>false</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14342,6 +14470,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower_inc(numrange(1.1,2.2))</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14353,6 +14492,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper_inc(numrange(1.1,2.2))</literal></entry>
<entry><literal>false</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14364,6 +14514,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower_inf('(,)'::daterange)</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14375,6 +14536,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper_inf('(,)'::daterange)</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14386,16 +14558,38 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
+ <function>multirange</function>(<type>anyrange</type>)
+ </literal>
+ </entry>
+ <entry><type>anymultirange</type></entry>
+ <entry>a multirange containing just the given range</entry>
+ <entry><literal>multirange('[1,2)'::int4range)</literal></entry>
+ <entry><literal>{[1,2)}</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -14714,6 +14908,44 @@ NULL baz</literallayout>(3 rows)</entry>
</entry>
</row>
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
<row>
<entry>
<indexterm>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index b75fb3a392..c8784bdce3 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -232,10 +239,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -267,6 +294,19 @@ SELECT int8range(1, 14, '(]');
-- Using NULL for either bound causes the range to be unbounded on that side.
SELECT numrange(NULL, 2.2);
+</programlisting>
+ </para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
</programlisting>
</para>
</sect2>
@@ -341,6 +381,11 @@ SELECT '[1.234, 5.678]'::floatrange;
function in this example.
</para>
+ <para>
+ When you define your own range you automatically get a corresponding
+ multirange type.
+ </para>
+
<para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index b5bc36c2bd..1217a6ddd0 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,12 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index cd56714968..c359f27c74 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -27,6 +27,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
@@ -36,6 +37,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
+ bool tryOriginal);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -809,31 +813,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
char *
makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
- char *arr = (char *) palloc(NAMEDATALEN);
- int namelen = strlen(typeName);
- int i;
+ char *arr;
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
-
- if (i >= NAMEDATALEN - 1)
+ arr = makeUniqueTypeName(typeName, typeNamespace, false);
+ if (arr == NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -904,3 +887,91 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char *buf;
+ char *mrname;
+ char *rangestr;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "_multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ char *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
+
+ buf = psprintf("%s%s%s", prefix, "multi", rangestr);
+ }
+ else
+ buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
+
+ /* clip it at NAMEDATALEN-1 bytes */
+ buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
+
+ mrname = makeUniqueTypeName(buf, typeNamespace, true);
+ if (mrname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mrname;
+}
+
+/*
+ * makeUniqueTypeName
+ * Generate a unique name for a prospective new type
+ *
+ * Given a typeName, return a new palloc'ed name by preprending underscores
+ * until a non-conflicting name results.
+ *
+ * If tryOriginal, first try with zero underscores.
+ */
+static char *
+makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
+{
+ int i;
+ int namelen;
+ char dest[NAMEDATALEN];
+
+ Assert(strlen(typeName) <= NAMEDATALEN - 1);
+
+ if (tryOriginal &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(typeName);
+
+ /*
+ * The idea is to prepend underscores as needed until we make a name that
+ * doesn't collide with anything ...
+ */
+ namelen = strlen(typeName);
+ for (i = 1; i < NAMEDATALEN - 1; i++)
+ {
+ dest[i - 1] = '_';
+ strlcpy(dest + i, typeName, NAMEDATALEN - i);
+ if (namelen + i >= NAMEDATALEN)
+ truncate_identifier(dest, NAMEDATALEN, false);
+
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(dest);
+ }
+
+ return NULL;
+}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 8891b1d564..dfb2edd3d0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -105,9 +105,14 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid,
+ Oid rangeArrayOid, Oid *castFuncOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -738,7 +743,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1279,6 +1285,11 @@ checkEnumOwner(HeapTuple tup)
/*
* DefineRange
* Registers a new range type.
+ *
+ * Perhaps it might be worthwhile to set pg_type.typelem to the base type,
+ * and likewise on multiranges to set it to the range type. But having a
+ * non-zero typelem is treated elsewhere as a synonym for being an array,
+ * and users might have queries with that same assumption.
*/
ObjectAddress
DefineRange(CreateRangeStmt *stmt)
@@ -1287,7 +1298,11 @@ DefineRange(CreateRangeStmt *stmt)
Oid typeNamespace;
Oid typoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1304,6 +1319,8 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
+ Oid castFuncOid;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1452,8 +1469,10 @@ DefineRange(CreateRangeStmt *stmt)
/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for ranges */
alignment = (subtypalign == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
- /* Allocate OID for array type */
+ /* Allocate OID for array type, its multirange, and its multirange array */
rangeArrayOid = AssignTypeArrayOid();
+ multirangeOid = AssignTypeMultirangeOid();
+ multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
address =
@@ -1491,9 +1510,46 @@ DefineRange(CreateRangeStmt *stmt)
Assert(typoid == InvalidOid || typoid == address.objectId);
typoid = address.objectId;
+ /* Create the multirange that goes with it */
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(multirangeOid, /* force assignment of this type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_RANGE, /* type-category (range type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ Assert(multirangeOid == mltrngaddress.objectId);
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
@@ -1534,8 +1590,53 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ multirangeOid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ multirangeOid, typoid, rangeArrayOid,
+ &castFuncOid);
+
+ /* Create cast from the range type to its multirange type */
+ CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1613,6 +1714,147 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ *
+ * Sets castFuncOid to the oid of the new constructor that can be used
+ * to cast from a range to a multirange.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
+ Oid *castFuncOid)
+{
+ ObjectAddress myself,
+ referenced;
+ oidvector *argtypes;
+ Datum allParamTypes;
+ ArrayType *allParameterTypes;
+ Datum paramModes;
+ ArrayType *parameterModes;
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ /* 0-arg constructor - for empty multiranges */
+ argtypes = buildoidvector(NULL, 0);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor0", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /*
+ * Make the constructor internally-dependent on the multirange type so
+ * that they go away silently when the type is dropped. Note that pg_dump
+ * depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+
+ /*
+ * 1-arg constructor - for casts
+ *
+ * In theory we shouldn't need both this and the vararg (n-arg) constructor,
+ * but having a separate 1-arg function lets us define casts against it.
+ */
+ argtypes = buildoidvector(&rangeOid, 1);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor1", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ true, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ *castFuncOid = myself.objectId;
+
+ /* n-arg constructor - vararg */
+ argtypes = buildoidvector(&rangeArrayOid, 1);
+ allParamTypes = ObjectIdGetDatum(rangeArrayOid);
+ allParameterTypes = construct_array(&allParamTypes,
+ 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
+ parameterModes = construct_array(¶mModes, 1, CHAROID,
+ 1, true, 'c');
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor2", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(allParameterTypes), /* allParameterTypes */
+ PointerGetDatum(parameterModes), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ pfree(allParameterTypes);
+ pfree(parameterModes);
+}
/*
* Find suitable I/O functions for a type.
@@ -2045,6 +2287,72 @@ AssignTypeArrayOid(void)
return type_array_oid;
}
+/*
+ * AssignTypeMultirangeOid
+ *
+ * Pre-assign the range type's multirange OID for use in pg_type.oid
+ */
+Oid
+AssignTypeMultirangeOid(void)
+{
+ Oid type_multirange_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
+
+ type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
+ binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_oid;
+}
+
+/*
+ * AssignTypeMultirangeArrayOid
+ *
+ * Pre-assign the range type's multirange array OID for use in pg_type.typarray
+ */
+Oid
+AssignTypeMultirangeArrayOid(void)
+{
+ Oid type_multirange_array_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
+
+ type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
+ binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_array_oid;
+}
+
/*-------------------------------------------------------------------
* DefineCompositeType
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 645e4aa4ce..91f80a0656 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -188,6 +188,7 @@ coerce_type(ParseState *pstate, Node *node,
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID ||
targetTypeId == ANYCOMPATIBLEARRAYOID ||
targetTypeId == ANYCOMPATIBLERANGEOID)
{
@@ -195,12 +196,12 @@ coerce_type(ParseState *pstate, Node *node,
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call the pseudotype's input
- * function, which will produce an error. Also, if what we have is a
- * domain over array, enum, or range, we have to relabel it to its
- * base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * the pseudotype's input function, which will produce an error. Also,
+ * if what we have is a domain over array, enum, range, or multirange,
+ * we have to relabel it to its base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1531,8 +1532,8 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* 1) All arguments declared ANYELEMENT must have the same datatype.
* 2) All arguments declared ANYARRAY must have the same datatype,
* which must be a varlena array type.
- * 3) All arguments declared ANYRANGE must have the same datatype,
- * which must be a range type.
+ * 3) All arguments declared ANYRANGE or ANYMULTIRANGE must be a range or
+ * multirange type, all derived from the same base datatype.
* 4) If there are arguments of more than one of these polymorphic types,
* the array element type and/or range subtype must be the same as each
* other and the same as the ANYELEMENT type.
@@ -1582,8 +1583,10 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid anycompatible_range_typeid = InvalidOid;
Oid anycompatible_range_typelem = InvalidOid;
+ Oid range_typelem = InvalidOid;
bool have_anynonarray = false;
bool have_anyenum = false;
bool have_anycompatible_nonarray = false;
@@ -1632,6 +1635,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
else if (decl_type == ANYCOMPATIBLEOID ||
decl_type == ANYCOMPATIBLENONARRAYOID)
{
@@ -1722,8 +1734,6 @@ check_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- Oid range_typelem;
-
range_typelem = get_range_subtype(range_typeid);
if (!OidIsValid(range_typelem))
return false; /* should be a range, but isn't */
@@ -1742,6 +1752,45 @@ check_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ Oid multirange_typelem;
+
+ multirange_typelem = get_multirange_range(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+ }
+
if (have_anynonarray)
{
/* require the element type to not be an array or domain over array */
@@ -1820,16 +1869,23 @@ check_generic_type_consistency(const Oid *actual_arg_types,
* 2) If return type is ANYARRAY, and any argument is ANYARRAY, use the
* argument's actual type as the function's return type.
* 3) Similarly, if return type is ANYRANGE, and any argument is ANYRANGE,
- * use the argument's actual type as the function's return type.
- * 4) Otherwise, if return type is ANYELEMENT or ANYARRAY, there should be
- * at least one ANYELEMENT, ANYARRAY, or ANYRANGE input; deduce the
- * return type from those inputs, or throw error if we can't.
- * 5) Otherwise, if return type is ANYRANGE, throw error. (There should
- * be at least one ANYRANGE input, since CREATE FUNCTION enforces that.)
- * 6) ANYENUM is treated the same as ANYELEMENT except that if it is used
+ * use the argument's actual type as the function's return type. Or
+ * if any argument is ANYMULTIRANGE, use its range type as the function's
+ * return type.
+ * 4) Otherwise, if return type is ANYMULTIRANGE, and any argument is
+ * ANYMULTIRANGE, use the argument's actual type as the function's return
+ * type. Or if any argument is ANYRANGE, use its multirange type as the
+ * function's return type.
+ * 5) Otherwise, if return type is ANYELEMENT or ANYARRAY, there should be
+ * at least one ANYELEMENT, ANYARRAY, ANYRANGE, or ANYMULTIRANGE input;
+ * deduce the return type from those inputs, or throw error if we can't.
+ * 6) Otherwise, if return type is ANYRANGE, throw error. (There should
+ * be at least one ANYRANGE or ANYMULTIRANGE input, since CREATE FUNCTION
+ * enforces that.)
+ * 7) ANYENUM is treated the same as ANYELEMENT except that if it is used
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
- * 7) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
+ * 8) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
* we add the extra condition that the ANYELEMENT type must not be an array.
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
* is an extra restriction if not.)
@@ -1882,10 +1938,13 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid anycompatible_typeid = InvalidOid;
Oid anycompatible_array_typeid = InvalidOid;
Oid anycompatible_range_typeid = InvalidOid;
Oid anycompatible_range_typelem = InvalidOid;
+ Oid range_typelem;
+ Oid multirange_typelem;
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
bool have_anycompatible_nonarray = (rettype == ANYCOMPATIBLENONARRAYOID);
@@ -1970,6 +2029,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ n_poly_args++;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_poly_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
else if (decl_type == ANYCOMPATIBLEOID ||
decl_type == ANYCOMPATIBLENONARRAYOID)
{
@@ -2109,8 +2188,6 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- Oid range_typelem;
-
range_typelem = get_range_subtype(range_typeid);
if (!OidIsValid(range_typelem))
ereport(ERROR,
@@ -2139,6 +2216,61 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(elem_typeid))));
}
}
+ else
+ range_typelem = InvalidOid;
+
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_multirange_range(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * if we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
+ }
+ else
+ multirange_typelem = InvalidOid;
if (!OidIsValid(elem_typeid))
{
@@ -2147,6 +2279,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -2327,6 +2460,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -2363,6 +2507,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYCOMPATIBLE use the appropriate type */
if (rettype == ANYCOMPATIBLEOID ||
rettype == ANYCOMPATIBLENONARRAYOID)
@@ -2414,20 +2574,36 @@ check_valid_polymorphic_signature(Oid ret_type,
const Oid *declared_arg_types,
int nargs)
{
- if (ret_type == ANYRANGEOID || ret_type == ANYCOMPATIBLERANGEOID)
+ if (ret_type == ANYRANGEOID || ret_type == ANYMULTIRANGEOID)
{
/*
- * ANYRANGE requires an ANYRANGE input, else we can't tell which of
- * several range types with the same element type to use. Likewise
- * for ANYCOMPATIBLERANGE.
+ * ANYRANGE and ANYMULTIRANGE require an ANYRANGE or ANYMULTIRANGE input,
+ * else we can't tell which of several range types with the same element
+ * type to use.
+ */
+ for (int i = 0; i < nargs; i++)
+ {
+ if (declared_arg_types[i] == ANYRANGEOID ||
+ declared_arg_types[i] == ANYMULTIRANGEOID)
+ return NULL; /* OK */
+ }
+ return psprintf(_("A result of type %s requires at least one input of type anyrange or anymultirange."),
+ format_type_be(ret_type));
+ }
+ else if (ret_type == ANYCOMPATIBLERANGEOID)
+ {
+ /*
+ * ANYCOMPATIBLERANGE requires an ANYCOMPATIBLERANGE input,
+ * else we can't tell which of several range types with the same element
+ * type to use.
*/
for (int i = 0; i < nargs; i++)
{
if (declared_arg_types[i] == ret_type)
return NULL; /* OK */
}
- return psprintf(_("A result of type %s requires at least one input of type %s."),
- format_type_be(ret_type), format_type_be(ret_type));
+ return psprintf(_("A result of type %s requires at least one input of type anycompatiblerange."),
+ format_type_be(ret_type));
}
else if (IsPolymorphicTypeFamily1(ret_type))
{
@@ -2438,7 +2614,7 @@ check_valid_polymorphic_signature(Oid ret_type,
return NULL; /* OK */
}
/* Keep this list in sync with IsPolymorphicTypeFamily1! */
- return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange."),
+ return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange."),
format_type_be(ret_type));
}
else if (IsPolymorphicTypeFamily2(ret_type))
@@ -2590,6 +2766,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..7118dd0034 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..da78227b01
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,2181 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ FmgrInfo typioproc; /* range type's I/O proc */
+ Oid typioparam; /* range type's I/O parameter */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData *get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks with either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str = NULL;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 1;
+ range_str_copy = pnstrdup(range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **)
+ repalloc(ranges, range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(InputFunctionCall(&cache->typioproc,
+ range_str_copy,
+ cache->typioparam,
+ typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->typioproc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: First a int32-sized count of ranges, followed by
+ * ranges in their native binary representation.
+ */
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ StringInfoData tmpbuf;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc(range_count * sizeof(RangeType *));
+
+ initStringInfo(&tmpbuf);
+ for (int i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+
+ resetStringInfo(&tmpbuf);
+ appendBinaryStringInfo(&tmpbuf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(ReceiveFunctionCall(&cache->typioproc,
+ &tmpbuf,
+ cache->typioparam,
+ typmod));
+ }
+ pfree(tmpbuf.data);
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, cache->typcache->rngtype,
+ range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (int i = 0; i < range_count; i++)
+ {
+ Datum range;
+
+ range = RangeTypePGetDatum(ranges[i]);
+ range = PointerGetDatum(SendFunctionCall(&cache->typioproc, range));
+
+ pq_sendint32(buf, VARSIZE(range) - VARHDRSZ);
+ pq_sendbytes(buf, VARDATA(range), VARSIZE(range) - VARHDRSZ);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ Oid typiofunc;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &typiofunc);
+
+ if (!OidIsValid(typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(typiofunc, &cache->typioproc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ bytelen += MAXALIGN(VARSIZE(ranges[i]));
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ memcpy(ptr, ranges[i], VARSIZE(ranges[i]));
+ ptr += MAXALIGN(VARSIZE(ranges[i]));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of arbitrary ranges into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ *
+ * Returns the number of slots actually used, which may be less than
+ * input_range_count but never more.
+ *
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType *multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor2(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_CARDINALITY_VIOLATION),
+ errmsg("multiranges cannot be constructed from multi-dimensional arrays")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Construct multirange value from a single range.
+ * It'd be nice if we could just use multirange_constructor2
+ * for this case, but we need a non-variadic single-arg function
+ * to let us define a CAST from a range to its multirange.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ RangeType *range;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ range = PG_GETARG_RANGE_P(0);
+
+ /* Make sure the range type matches. */
+ rngtypid = RangeTypeGetOid(range);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 1, &range));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ elog(ERROR, /* can't happen */
+ "niladic multirange constructor must not receive arguments");
+}
+
+
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+multirange_union(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
+}
+
+/* multirange minus */
+Datum
+multirange_minus(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+multirange_intersect(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+ /*-----------------------------------------------
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1,
+ * but one extra won't hurt.
+ *-----------------------------------------------
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType *mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges1[range_count1 - 1],
+ ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype,
+ ranges1[0],
+ ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1, MultirangeType *mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
+ return false;
+
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
+ return false;
+ }
+
+ /* All ranges in mr2 are satisfied */
+ return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 18f2ee8226..248ea7f44a 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -51,6 +51,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
+Datum
+binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
+binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_array_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
Datum
binary_upgrade_set_next_toast_pg_type_oid(PG_FUNCTION_ARGS)
{
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 3d6b2f9093..4fcd8ce6de 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -227,6 +228,30 @@ anycompatiblerange_out(PG_FUNCTION_ARGS)
return range_out(fcinfo);
}
+/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
/*
* void
*
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 01ad8bc240..0af3c67296 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -431,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -452,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -475,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -485,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -495,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -505,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -515,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -957,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -969,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -993,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1101,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1110,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1132,9 +1180,81 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
}
+/* range, range -> range, range functions */
+
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
+ {
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
+ }
+
+ return false;
+}
+
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
+}
+
+
/* Btree support */
/* btree comparator */
@@ -1144,12 +1264,6 @@ range_cmp(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
- RangeBound lower1,
- lower2;
- RangeBound upper1,
- upper2;
- bool empty1,
- empty2;
int cmp;
check_stack_depth(); /* recurses when subtype is a range type */
@@ -1160,6 +1274,28 @@ range_cmp(PG_FUNCTION_ARGS)
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+ cmp = range_cmp_internal(typcache, r1, r2);
+
+ PG_FREE_IF_COPY(r1, 0);
+ PG_FREE_IF_COPY(r2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
@@ -1177,10 +1313,7 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
- PG_FREE_IF_COPY(r1, 0);
- PG_FREE_IF_COPY(r2, 1);
-
- PG_RETURN_INT32(cmp);
+ return cmp;
}
/* inequality operators using the range_cmp function */
@@ -1218,13 +1351,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1363,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1404,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1433,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1471,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1769,6 +1916,21 @@ range_get_flags(const RangeType *range)
return *((char *) range + VARSIZE(range) - 1);
}
+/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
@@ -1937,6 +2099,46 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
b1->val, b2->val));
}
+/*
+ * qsort callback for sorting ranges.
+ *
+ * Two empty ranges compare equal; an empty range sorts to the left of any
+ * non-empty range. Two non-empty ranges are sorted by lower bound first
+ * and by upper bound next.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
/*
* Build an empty range value of the type indicated by the typcache entry.
*/
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 0a6db0d478..6e9a48b314 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2529,6 +2529,16 @@ type_is_range(Oid typid)
return (get_typtype(typid) == TYPTYPE_RANGE);
}
+/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
/*
* get_type_category_preferred
*
@@ -3184,7 +3194,7 @@ get_namespace_name_or_temp(Oid nspid)
return get_namespace_name(nspid);
}
-/* ---------- PG_RANGE CACHE ---------- */
+/* ---------- PG_RANGE CACHES ---------- */
/*
* get_range_subtype
@@ -3237,6 +3247,56 @@ get_range_collation(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngmultitypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_multirange_range
+ * Returns the range type of a given multirange
+ *
+ * Returns InvalidOid if the type is not a multirange.
+ */
+Oid
+get_multirange_range(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGEMULTIRANGE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 53d9ddf159..7654331a59 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -651,6 +651,18 @@ static const struct cachedesc cacheinfo[] = {
},
64
},
+ {RangeRelationId, /* RANGEMULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngmultitypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+
{RangeRelationId, /* RANGETYPE */
RangeTypidIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 854f133f9b..f1d3cb97d0 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -286,6 +286,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -302,6 +303,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -553,8 +557,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -591,7 +595,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -616,7 +620,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -641,7 +645,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -693,6 +697,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -737,6 +748,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -816,6 +834,16 @@ lookup_type_cache(Oid type_id, int flags)
(void) lookup_type_cache(typentry->rngelemtype->type_id, 0);
}
+ /*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
/*
* If requested, get information about a domain type
*/
@@ -927,6 +955,22 @@ load_rangetype_info(TypeCacheEntry *typentry)
typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
}
+/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Oid rangetypeOid;
+
+ rangetypeOid = get_multirange_range(typentry->type_id);
+ if (!OidIsValid(rangetypeOid))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
/*
* load_domaintype_info --- helper routine to set up domain constraint info
@@ -1527,11 +1571,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1574,6 +1618,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 78ed857203..ce09d0d128 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -35,6 +35,7 @@ typedef struct polymorphic_actuals
Oid anyelement_type; /* anyelement mapping, if known */
Oid anyarray_type; /* anyarray mapping, if known */
Oid anyrange_type; /* anyrange mapping, if known */
+ Oid anymultirange_type; /* anymultirange mapping, if known */
} polymorphic_actuals;
static void shutdown_MultiFuncCall(Datum arg);
@@ -46,6 +47,7 @@ static TypeFuncClass internal_get_result_type(Oid funcid,
static void resolve_anyelement_from_others(polymorphic_actuals *actuals);
static void resolve_anyarray_from_others(polymorphic_actuals *actuals);
static void resolve_anyrange_from_others(polymorphic_actuals *actuals);
+static void resolve_anymultirange_from_others(polymorphic_actuals *actuals);
static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
oidvector *declared_args,
Node *call_expr);
@@ -503,6 +505,34 @@ resolve_anyelement_from_others(polymorphic_actuals *actuals)
format_type_be(range_base_type))));
actuals->anyelement_type = range_typelem;
}
+ else if (OidIsValid(actuals->anymultirange_type))
+ {
+ /* Use the element type based on the multirange type */
+ Oid multirange_base_type;
+ Oid multirange_typelem;
+ Oid range_base_type;
+ Oid range_typelem;
+
+ multirange_base_type = getBaseType(actuals->anymultirange_type);
+ multirange_typelem = get_multirange_range(multirange_base_type);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_base_type))));
+
+ range_base_type = getBaseType(multirange_typelem);
+ range_typelem = get_range_subtype(range_base_type);
+
+ if (!OidIsValid(range_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s does not contain a range type but type %s",
+ "anymultirange",
+ format_type_be(range_base_type))));
+ actuals->anyelement_type = range_typelem;
+ }
else
elog(ERROR, "could not determine polymorphic type");
}
@@ -540,10 +570,54 @@ static void
resolve_anyrange_from_others(polymorphic_actuals *actuals)
{
/*
- * We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types with the same subtype.
+ * We can't deduce a range type from other polymorphic array or base types,
+ * because there may be multiple range types with the same subtype, but we
+ * can deduce it from a polymorphic multirange type.
*/
- elog(ERROR, "could not determine polymorphic type");
+ if (OidIsValid(actuals->anymultirange_type))
+ {
+ /* Use the element type based on the multirange type */
+ Oid multirange_base_type = getBaseType(actuals->anymultirange_type);
+ Oid multirange_typelem =
+ get_multirange_range(multirange_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_base_type))));
+ actuals->anyrange_type = multirange_typelem;
+ }
+ else
+ elog(ERROR, "could not determine polymorphic type");
+}
+
+/*
+ * Resolve actual type of ANYMULTIRANGE from other polymorphic inputs
+ */
+static void
+resolve_anymultirange_from_others(polymorphic_actuals *actuals)
+{
+ /*
+ * We can't deduce a multirange type from polymorphic array or base types,
+ * because there may be multiple range types with the same subtype, but we
+ * can deduce it from a polymorphic range type.
+ */
+ if (OidIsValid(actuals->anyrange_type))
+ {
+ Oid range_base_type = getBaseType(actuals->anyrange_type);
+ Oid multirange_typeid = get_range_multirange(range_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(actuals->anyrange_type))));
+ actuals->anymultirange_type = multirange_typeid;
+ }
+ else
+ elog(ERROR, "could not determine polymorphic type");
}
/*
@@ -566,6 +640,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anycompatible_result = false;
bool have_anycompatible_array_result = false;
bool have_anycompatible_range_result = false;
@@ -594,6 +669,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
have_polymorphic_result = true;
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_polymorphic_result = true;
+ have_anymultirange_result = true;
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
have_polymorphic_result = true;
@@ -660,6 +739,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
}
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ {
+ poly_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ return false;
+ }
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
if (!OidIsValid(anyc_actuals.anyelement_type))
@@ -703,6 +791,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&poly_actuals);
+
if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
resolve_anyelement_from_others(&anyc_actuals);
@@ -780,6 +871,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ poly_actuals.anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
TupleDescInitEntry(tupdesc, i + 1,
@@ -834,6 +933,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anycompatible_result = false;
bool have_anycompatible_array_result = false;
bool have_anycompatible_range_result = false;
@@ -912,6 +1012,24 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = poly_actuals.anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
+ have_anymultirange_result = true;
+ }
+ else
+ {
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ {
+ poly_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ return false;
+ }
+ argtypes[i] = poly_actuals.anymultirange_type;
+ }
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
@@ -988,6 +1106,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&poly_actuals);
+
if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
resolve_anyelement_from_others(&anyc_actuals);
@@ -1013,6 +1134,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = poly_actuals.anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = poly_actuals.anymultirange_type;
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
argtypes[i] = anyc_actuals.anyelement_type;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 408637cfec..cf4ce0a5a6 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -276,7 +276,8 @@ static void dumpSearchPath(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type);
+ bool force_array_type,
+ bool include_multirange_type);
static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
static void binary_upgrade_set_pg_class_oids(Archive *fout,
@@ -1648,7 +1649,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4416,16 +4417,49 @@ append_depends_on_extension(Archive *fout,
}
}
+static Oid
+get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+{
+ /*
+ * If the old version didn't assign an array type, but the new version
+ * does, we must select an unused type OID to assign. This currently only
+ * happens for domains, when upgrading pre-v11 to v11 and up.
+ *
+ * Note: local state here is kind of ugly, but we must have some, since we
+ * mustn't choose the same unused OID more than once.
+ */
+ static Oid next_possible_free_oid = FirstNormalObjectId;
+ PGresult *res;
+ bool is_dup;
+
+ do
+ {
+ ++next_possible_free_oid;
+ printfPQExpBuffer(upgrade_query,
+ "SELECT EXISTS(SELECT 1 "
+ "FROM pg_catalog.pg_type "
+ "WHERE oid = '%u'::pg_catalog.oid);",
+ next_possible_free_oid);
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+ is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
+ PQclear(res);
+ } while (is_dup);
+
+ return next_possible_free_oid;
+}
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type)
+ bool force_array_type,
+ bool include_multirange_type)
{
PQExpBuffer upgrade_query = createPQExpBuffer();
PGresult *res;
Oid pg_type_array_oid;
+ Oid pg_type_multirange_oid;
+ Oid pg_type_multirange_array_oid;
appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
@@ -4446,33 +4480,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQclear(res);
if (!OidIsValid(pg_type_array_oid) && force_array_type)
- {
- /*
- * If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
- *
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
- */
- static Oid next_possible_free_oid = FirstNormalObjectId;
- bool is_dup;
-
- do
- {
- ++next_possible_free_oid;
- printfPQExpBuffer(upgrade_query,
- "SELECT EXISTS(SELECT 1 "
- "FROM pg_catalog.pg_type "
- "WHERE oid = '%u'::pg_catalog.oid);",
- next_possible_free_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
- is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
- PQclear(res);
- } while (is_dup);
-
- pg_type_array_oid = next_possible_free_oid;
- }
+ pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
if (OidIsValid(pg_type_array_oid))
{
@@ -4483,6 +4491,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
pg_type_array_oid);
}
+ /*
+ * Pre-set the multirange type oid and its own array type oid.
+ */
+ if (include_multirange_type)
+ {
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBuffer(upgrade_query,
+ "SELECT t.oid, t.typarray "
+ "FROM pg_catalog.pg_type t "
+ "JOIN pg_catalog.pg_range r "
+ "ON t.oid = r.rngmultitypid "
+ "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
+ pg_type_oid);
+
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+
+ pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+ pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
+
+ PQclear(res);
+ }
+ else
+ {
+ pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ }
+
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_oid);
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_array_oid);
+ }
+
destroyPQExpBuffer(upgrade_query);
}
@@ -4516,7 +4564,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
- pg_type_oid, false);
+ pg_type_oid, false, false);
if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
{
@@ -5065,6 +5113,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
@@ -8203,9 +8256,12 @@ getProcLangs(Archive *fout, int *numProcLangs)
/*
* getCasts
- * get basic information about every cast in the system
+ * get basic information about most casts in the system
*
* numCasts is set to the number of casts read in
+ *
+ * Skip casts from a range to its multirange, since we'll create those
+ * automatically.
*/
CastInfo *
getCasts(Archive *fout, int *numCasts)
@@ -8223,7 +8279,20 @@ getCasts(Archive *fout, int *numCasts)
int i_castcontext;
int i_castmethod;
- if (fout->remoteVersion >= 80400)
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBufferStr(query, "SELECT tableoid, oid, "
+ "castsource, casttarget, castfunc, castcontext, "
+ "castmethod "
+ "FROM pg_cast c "
+ "WHERE NOT EXISTS ( "
+ "SELECT 1 FROM pg_range r "
+ "WHERE c.castsource = r.rngtypid "
+ "AND c.casttarget = r.rngmultitypid "
+ ") "
+ "ORDER BY 3,4");
+ }
+ else if (fout->remoteVersion >= 80400)
{
appendPQExpBufferStr(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
@@ -10411,7 +10480,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
qualtypname);
@@ -10537,7 +10606,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, true);
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
qualtypname);
@@ -10643,7 +10712,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
qualtypname);
@@ -10848,7 +10917,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
@@ -11035,7 +11104,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- true); /* force array type */
+ true, /* force array type */
+ false); /* force multirange type */
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -11223,7 +11293,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
{
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
}
@@ -11497,7 +11567,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
stinfo->baseType->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
fmtQualifiedDumpable(stinfo));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 3e11166615..0b717412b4 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -176,6 +176,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 70157cf90a..c262265b95 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -139,8 +139,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 12d94fe1b3..01d95117cd 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,6 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 8be303870f..566f9364c5 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -327,6 +327,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_rngmultitypid_index, 8001, on pg_range using btree(rngmultitypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index ffabe275c0..03bab74253 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..827ac4f03c 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index cef63b2a71..5ac1c478cb 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -285,6 +285,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
@@ -445,6 +447,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
@@ -1266,12 +1273,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index 01c5328ddd..f555af90ac 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -526,4 +526,17 @@
{ castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
castcontext => 'e', castmethod => 'f' },
+# range to multirange
+{ castsource => 'int4range', casttarget => 'int4multirange', castfunc => 'int4multirange(int4range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'int8range', casttarget => 'int8multirange', castfunc => 'int8multirange(int8range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'numrange', casttarget => 'nummultirange', castfunc => 'nummultirange(numrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'daterange', casttarget => 'datemultirange', castfunc => 'datemultirange(daterange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tsrange', casttarget => 'tsmultirange', castfunc => 'tsmultirange(tsrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tstzrange', casttarget => 'tstzmultirange', castfunc => 'tstzmultirange(tstzrange)',
+ castcontext => 'e', castmethod => 'f' },
]
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index ab2f50c9eb..eebebbcdb4 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 65c7fedf23..08cda3ba36 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3302,5 +3302,174 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'matchingsel', oprjoin => 'matchingjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 26227df216..901fd7b303 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a649e44d08..d6afad645b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9718,6 +9718,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9767,6 +9771,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9835,6 +9846,258 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8114',
+ proname => 'multirange_union', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union' },
+{ oid => '8120',
+ proname => 'multirange_minus', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus' },
+{ oid => '8126',
+ proname => 'multirange_intersect', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8146', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 't',
+ prorettype => 'int4multirange', proargtypes => 'int4range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8147', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 't',
+ prorettype => 'nummultirange', proargtypes => 'numrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8148', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 't',
+ prorettype => 'tsmultirange', proargtypes => 'tsrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8149', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 't',
+ prorettype => 'tstzmultirange', proargtypes => 'tstzrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8150', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 't',
+ prorettype => 'datemultirange', proargtypes => 'daterange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8151', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 't',
+ prorettype => 'int8multirange', proargtypes => 'int8range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8152', descr => 'anymultirange cast',
+ proname => 'multirange', proisstrict => 't',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
@@ -10217,6 +10480,14 @@
proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
+{ oid => '8144', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
+{ oid => '8145', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
{ oid => '3585', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index 479754c245..10060255c9 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ rngmultitypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', rngmultitypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', rngmultitypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', rngmultitypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ rngmultitypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ rngmultitypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index 98172bb1b6..60a8fde518 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -34,6 +34,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* OID of range's element type (subtype) */
Oid rngsubtype BKI_LOOKUP(pg_type);
+ /* OID of the range's multirange type */
+ Oid rngmultitypid BKI_LOOKUP(pg_type);
+
/* collation for this range type, or 0 */
Oid rngcollation BKI_DEFAULT(0);
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 2e6110e3f2..cb3d159bf1 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -491,6 +491,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -620,5 +654,10 @@
typtype => 'p', typcategory => 'P', typinput => 'anycompatiblerange_in',
typoutput => 'anycompatiblerange_out', typreceive => '-', typsend => '-',
typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 7b37562648..f1818d9a52 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -263,6 +263,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -304,7 +305,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#define IsPolymorphicTypeFamily2(typid) \
((typid) == ANYCOMPATIBLEOID || \
@@ -369,4 +371,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 23130895af..989914dfe1 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
+extern Oid AssignTypeMultirangeOid(void);
+extern Oid AssignTypeMultirangeArrayOid(void);
extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 374f57fb43..f2e73eabb0 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -156,6 +156,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -182,6 +183,8 @@ extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
extern Oid get_range_collation(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_multirange_range(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
extern bool get_index_isreplident(Oid index_oid);
extern bool get_index_isvalid(Oid index_oid);
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..b270eb7f51
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the count are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern MultirangeType *multirange_minus_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType *multirange_intersect_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType *range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType *make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType *make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 0cbbf09033..204aee4054 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
@@ -92,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -115,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -125,6 +141,7 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
@@ -132,8 +149,12 @@ extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f27b73d76d..e8f393520a 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -79,6 +79,7 @@ enum SysCacheIdentifier
PUBLICATIONOID,
PUBLICATIONREL,
PUBLICATIONRELMAP,
+ RANGEMULTIRANGE,
RANGETYPE,
RELNAMENSP,
RELOID,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index cdd20e56d7..33f332a141 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -100,6 +100,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_canonical_finfo; /* canonicalization function, if any */
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
+ /*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype; /* multirange's range underlying type */
+
/*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
@@ -143,6 +148,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 828ff5a288..1e3ff02289 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -513,6 +513,8 @@ do_compile(FunctionCallInfo fcinfo,
else if (rettypeid == ANYRANGEOID ||
rettypeid == ANYCOMPATIBLERANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY or ANYCOMPATIBLE */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2089,6 +2091,7 @@ build_datatype(HeapTuple typeTup, int32 typmod,
case TYPTYPE_BASE:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
+ case TYPTYPE_MULTIRANGE:
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_COMPOSITE:
@@ -2507,6 +2510,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYCOMPATIBLERANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..8232795148 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -140,6 +140,7 @@ owner of sequence deptest_a_seq
owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
+owner of type deptest_multirange
owner of type deptest_range
owner of table deptest2
owner of sequence ss1
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..23a06b301f
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,2395 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+ int4multirange
+----------------
+ {}
+(1 row)
+
+select int4range(1, 3)::int4multirange;
+ int4range
+-----------
+ {[1,3)}
+(1 row)
+
+select int4range(1, null)::int4multirange;
+ int4range
+-----------
+ {[1,)}
+(1 row)
+
+select int4range(null, null)::int4multirange;
+ int4range
+-----------
+ {(,)}
+(1 row)
+
+select 'empty'::textrange::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textrange('a', 'c')::textmultirange;
+ textrange
+-----------
+ {[a,c)}
+(1 row)
+
+select textrange('a', null)::textmultirange;
+ textrange
+-----------
+ {[a,)}
+(1 row)
+
+select textrange(null, null)::textmultirange;
+ textrange
+-----------
+ {(,)}
+(1 row)
+
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT nummultirange() + nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT nummultirange() - nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() * nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+ mr_polymorphic
+----------------
+ {[1,4)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 2efd7d7ec7..c02443ee40 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -206,6 +213,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -224,6 +233,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -335,7 +346,8 @@ WHERE p1.prorettype IN
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+----------------
@@ -350,20 +362,25 @@ ORDER BY 2;
3532 | enum_recv
(9 rows)
--- anyrange is tighter than the rest, can only resolve from anyrange input
+-- anyrange and anymultirange are tighter than the rest, can only resolve
+-- from each other
SELECT p1.oid, p1.proname
FROM pg_proc as p1
-WHERE p1.prorettype = 'anyrange'::regtype
+WHERE p1.prorettype IN ('anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
- 'anyrange'::regtype = ANY (p1.proargtypes)
+ ('anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
+ 8002 | anymultirange_in
3832 | anyrange_in
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(4 rows)
+(7 rows)
-- similarly for the anycompatible family
SELECT p1.oid, p1.proname
@@ -2181,13 +2198,14 @@ ORDER BY 1, 2, 3;
| float_ops | float4_ops | real
| float_ops | float8_ops | double precision
| jsonb_ops | jsonb_ops | jsonb
+ | multirange_ops | multirange_ops | anymultirange
| numeric_ops | numeric_ops | numeric
| range_ops | range_ops | anyrange
| record_image_ops | record_image_ops | record
| record_ops | record_ops | record
| tsquery_ops | tsquery_ops | tsquery
| tsvector_ops | tsvector_ops | tsvector
-(14 rows)
+(15 rows)
-- **************** pg_index ****************
-- Look for illegal values in pg_index fields.
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index d0a6b630b8..65827c03e6 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -1811,7 +1811,7 @@ begin
return array[x + 1, x + 2];
end$$ language plpgsql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function f1(x anyrange) returns anyarray as $$
begin
return array[lower(x), upper(x)];
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index d86a7004a6..e087db3ea6 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -61,7 +61,7 @@ create function polyf(x anyelement) returns anyrange as $$
select array[x + 1, x + 2]
$$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function polyf(x anyrange) returns anyarray as $$
select array[lower(x), upper(x)]
$$ language sql;
@@ -231,7 +231,7 @@ CREATE AGGREGATE myaggp01a(*) (SFUNC = stfnp, STYPE = int4[],
CREATE AGGREGATE myaggp02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- N P
-- should CREATE
CREATE AGGREGATE myaggp03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -243,11 +243,11 @@ CREATE AGGREGATE myaggp03b(*) (SFUNC = stfp, STYPE = int4[],
CREATE AGGREGATE myaggp04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp04b(*) (SFUNC = stfp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- Case2 (R = P) && ((B = P) || (B = N))
-- -------------------------------------
-- S tf1 B tf2
@@ -302,13 +302,13 @@ ERROR: function tfp(integer[], anyelement) does not exist
CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N N P
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N P N
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -324,21 +324,21 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P N P
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P P N
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p,
@@ -368,11 +368,11 @@ CREATE AGGREGATE myaggn01b(*) (SFUNC = stfnp, STYPE = int4[],
CREATE AGGREGATE myaggn02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn02b(*) (SFUNC = stfnp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- N P
-- should CREATE
CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -382,7 +382,7 @@ CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
CREATE AGGREGATE myaggn04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- Case4 (R = N) && ((B = P) || (B = N))
-- -------------------------------------
-- S tf1 B tf2
@@ -436,21 +436,21 @@ ERROR: function tfp(integer[], anyelement) does not exist
CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N N P
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N P N
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -472,13 +472,13 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P N P
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P P N
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p,
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 7eced28452..71e2769f44 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -1556,7 +1556,7 @@ DROP FUNCTION dup(anyelement);
CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE FUNCTION dup (f1 anycompatible, f2 anycompatiblearray, f3 out anycompatible, f4 out anycompatiblearray)
AS 'select $1, $2' LANGUAGE sql;
SELECT dup(22, array[44]);
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index c421f5394f..763e3819a5 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1371,12 +1389,12 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function range_add_bounds(anyrange)
returns anyelement as 'select lower($1) + upper($1)' language sql;
select range_add_bounds(int4range(1, 17));
@@ -1508,6 +1526,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1525,6 +1544,16 @@ select * from outparam2_succeed(int4range(1,11));
{1,11} | {11,1}
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1547,14 +1576,14 @@ select * from table_succeed(int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 192445878d..d9ce961be2 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
numrange_test|t
onek|t
onek2|t
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 274130e706..8df1ae47e4 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -195,18 +195,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -240,17 +241,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -318,18 +320,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -363,17 +366,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -631,3 +635,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
+ rngtypid | rngsubtype | rngmultitypid
+----------+------------+---------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index a98dba7b2f..18c25f4d8b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 3f66e0b859..dca9f91afb 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..0f70919e1a
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,630 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+select int4range(1, 3)::int4multirange;
+select int4range(1, null)::int4multirange;
+select int4range(null, null)::int4multirange;
+select 'empty'::textrange::textmultirange;
+select textrange('a', 'c')::textmultirange;
+select textrange('a', null)::textmultirange;
+select textrange(null, null)::textmultirange;
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT nummultirange() + nummultirange();
+SELECT nummultirange() + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT nummultirange() - nummultirange();
+SELECT nummultirange() - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+SELECT nummultirange() * nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 9a1ea3d999..69c4859102 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -279,16 +290,19 @@ WHERE p1.prorettype IN
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
--- anyrange is tighter than the rest, can only resolve from anyrange input
+-- anyrange and anymultirange are tighter than the rest, can only resolve
+-- from each other
SELECT p1.oid, p1.proname
FROM pg_proc as p1
-WHERE p1.prorettype = 'anyrange'::regtype
+WHERE p1.prorettype IN ('anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
- 'anyrange'::regtype = ANY (p1.proargtypes)
+ ('anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- similarly for the anycompatible family
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 4048b1d185..0276d19136 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -528,6 +532,7 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
@@ -539,6 +544,13 @@ create function outparam2_succeed(r anyrange, out lu anyarray, out ul anyarray)
select * from outparam2_succeed(int4range(1,11));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index 4b492ce062..07879d9a57 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -153,7 +153,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -182,7 +182,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -234,7 +234,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -263,7 +263,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -467,3 +467,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 34623523a7..a2481e3837 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1362,6 +1362,9 @@ MultiXactMember
MultiXactOffset
MultiXactStateData
MultiXactStatus
+MultirangeIOData
+MultirangeParseState
+MultirangeType
MyData
NDBOX
NODE
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-14 18:13 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-17 04:52 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-19 20:41 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-19 20:43 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-20 05:24 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-23 23:32 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-24 04:23 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2020-04-04 23:10 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-04-05 22:13 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-04-05 22:14 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
1 sibling, 1 reply; 152+ messages in thread
From: Alvaro Herrera @ 2020-04-05 22:13 UTC (permalink / raw)
To: Paul Jungwirth <pj@illuminatedcomputing.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
v17 is a rebase fixing a minor parse_coerce.c edit; v16 lasted little
:-( I chose to change the wording of the conflicting comment in
enforce_generic_type_consistency():
* 3) Similarly, if return type is ANYRANGE or ANYMULTIRANGE, and any
* argument is ANYRANGE or ANYMULTIRANGE, use that argument's
* actual type, range type or multirange type as the function's return
* type.
This wording is less precise, in that it doesn't say exactly which of
the three types is the actual result for each of the possible four cases
(r->r, r->m, m->m, m->r) but I think it should be straightforward.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-14 18:13 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-17 04:52 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-19 20:41 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-19 20:43 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-20 05:24 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-23 23:32 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-24 04:23 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2020-04-04 23:10 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-04-05 22:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-04-05 22:14 ` Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 0 replies; 152+ messages in thread
From: Alvaro Herrera @ 2020-04-05 22:14 UTC (permalink / raw)
To: Paul Jungwirth <pj@illuminatedcomputing.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
On 2020-Apr-05, Alvaro Herrera wrote:
> v17 is a rebase fixing a minor parse_coerce.c edit; v16 lasted little
> :-( I chose to change the wording of the conflicting comment in
> enforce_generic_type_consistency():
Hm, attached.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
[text/x-diff] v17-multirange.patch (330.9K, ../../20200405221406.GA22423@alvherre.pgsql/2-v17-multirange.patch)
download | inline diff:
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 930aeb767c..febb02e80a 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -285,6 +285,14 @@
</entry>
</row>
+ <row>
+ <entry><type>anymultirange</type></entry>
+ <entry>Simple</entry>
+ <entry>Indicates that a function accepts any multirange data type
+ (see <xref linkend="rangetypes"/>)
+ </entry>
+ </row>
+
<row>
<entry><type>anycompatible</type></entry>
<entry>Common</entry>
@@ -343,17 +351,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type> or <type>anyarray</type>,
- the actual range type in the <type>anyrange</type> positions must be a
- range whose subtype is the same type appearing in
- the <type>anyelement</type> positions and the same as the element type
- of the <type>anyarray</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -362,6 +368,19 @@
be an enum type.
</para>
+ <para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type> or <type>anyarray</type>,
+ the actual range type in the <type>anyrange</type> positions must be a
+ range whose subtype is the same type appearing in
+ the <type>anyelement</type> positions and the same as the element type
+ of the <type>anyarray</type> positions.
+ If there are positions declared <type>anymultirange</type>,
+ their actual multirange type must contain ranges matching parameters declared
+ <type>anyrange</type> and base elements matching parameters declared
+ <type>anyelement</type> and <type>anyarray</type>.
+ </para>
+
<para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 4d88b45e72..b2168f3801 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -13609,7 +13609,7 @@ SELECT NULLIF(value, '(none)') ...
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14080,12 +14080,14 @@ NULL baz</literallayout>(3 rows)</entry>
<title>Range Functions and Operators</title>
<para>
- See <xref linkend="rangetypes"/> for an overview of range types.
+ See <xref linkend="rangetypes"/> for an overview of range and multirange types.
</para>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14103,134 +14105,215 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
- <entry><literal>f</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">f</literallayout></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
- <entry><literal>[5,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)
+'{[5,10)}'::nummultirange + '{[15,20)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,20)
+{[5,10), [15,20)}</literallayout></entry>
</row>
<row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
- <entry><literal>[10,15)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)
+'{[5,15)}'::int8multirange * '{[10,20)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[10,15)
+{[10,15)}</literallayout></entry>
</row>
<row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
- <entry><literal>[5,10)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)
+'{[5,20)}'::int8multirange - '{[10,15)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,10)
+{[5,10), [15,20)}</literallayout></entry>
</row>
</tbody>
@@ -14248,19 +14331,31 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. There are separate operators for union and difference that take
+ multirange parameters and return a multirange, and they do not fail even if
+ their arguments are disjoint. So if you need a union or difference operation
+ for ranges that may be disjoint, you can avoid errors by first casting your
+ ranges to multiranges.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14309,6 +14404,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower(numrange(1.1,2.2))</literal></entry>
<entry><literal>1.1</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14320,6 +14426,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper(numrange(1.1,2.2))</literal></entry>
<entry><literal>2.2</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14331,6 +14448,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>isempty(numrange(1.1,2.2))</literal></entry>
<entry><literal>false</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14342,6 +14470,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower_inc(numrange(1.1,2.2))</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14353,6 +14492,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper_inc(numrange(1.1,2.2))</literal></entry>
<entry><literal>false</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14364,6 +14514,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower_inf('(,)'::daterange)</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14375,6 +14536,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper_inf('(,)'::daterange)</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14386,16 +14558,38 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
+ <function>multirange</function>(<type>anyrange</type>)
+ </literal>
+ </entry>
+ <entry><type>anymultirange</type></entry>
+ <entry>a multirange containing just the given range</entry>
+ <entry><literal>multirange('[1,2)'::int4range)</literal></entry>
+ <entry><literal>{[1,2)}</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -14714,6 +14908,44 @@ NULL baz</literallayout>(3 rows)</entry>
</entry>
</row>
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
<row>
<entry>
<indexterm>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index b75fb3a392..c8784bdce3 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -232,10 +239,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -267,6 +294,19 @@ SELECT int8range(1, 14, '(]');
-- Using NULL for either bound causes the range to be unbounded on that side.
SELECT numrange(NULL, 2.2);
+</programlisting>
+ </para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
</programlisting>
</para>
</sect2>
@@ -341,6 +381,11 @@ SELECT '[1.234, 5.678]'::floatrange;
function in this example.
</para>
+ <para>
+ When you define your own range you automatically get a corresponding
+ multirange type.
+ </para>
+
<para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index b5bc36c2bd..1217a6ddd0 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,12 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index cd56714968..c359f27c74 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -27,6 +27,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
@@ -36,6 +37,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
+ bool tryOriginal);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -809,31 +813,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
char *
makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
- char *arr = (char *) palloc(NAMEDATALEN);
- int namelen = strlen(typeName);
- int i;
+ char *arr;
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
-
- if (i >= NAMEDATALEN - 1)
+ arr = makeUniqueTypeName(typeName, typeNamespace, false);
+ if (arr == NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -904,3 +887,91 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char *buf;
+ char *mrname;
+ char *rangestr;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "_multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ char *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
+
+ buf = psprintf("%s%s%s", prefix, "multi", rangestr);
+ }
+ else
+ buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
+
+ /* clip it at NAMEDATALEN-1 bytes */
+ buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
+
+ mrname = makeUniqueTypeName(buf, typeNamespace, true);
+ if (mrname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mrname;
+}
+
+/*
+ * makeUniqueTypeName
+ * Generate a unique name for a prospective new type
+ *
+ * Given a typeName, return a new palloc'ed name by preprending underscores
+ * until a non-conflicting name results.
+ *
+ * If tryOriginal, first try with zero underscores.
+ */
+static char *
+makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
+{
+ int i;
+ int namelen;
+ char dest[NAMEDATALEN];
+
+ Assert(strlen(typeName) <= NAMEDATALEN - 1);
+
+ if (tryOriginal &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(typeName);
+
+ /*
+ * The idea is to prepend underscores as needed until we make a name that
+ * doesn't collide with anything ...
+ */
+ namelen = strlen(typeName);
+ for (i = 1; i < NAMEDATALEN - 1; i++)
+ {
+ dest[i - 1] = '_';
+ strlcpy(dest + i, typeName, NAMEDATALEN - i);
+ if (namelen + i >= NAMEDATALEN)
+ truncate_identifier(dest, NAMEDATALEN, false);
+
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(dest);
+ }
+
+ return NULL;
+}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 8891b1d564..dfb2edd3d0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -105,9 +105,14 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid,
+ Oid rangeArrayOid, Oid *castFuncOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -738,7 +743,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1279,6 +1285,11 @@ checkEnumOwner(HeapTuple tup)
/*
* DefineRange
* Registers a new range type.
+ *
+ * Perhaps it might be worthwhile to set pg_type.typelem to the base type,
+ * and likewise on multiranges to set it to the range type. But having a
+ * non-zero typelem is treated elsewhere as a synonym for being an array,
+ * and users might have queries with that same assumption.
*/
ObjectAddress
DefineRange(CreateRangeStmt *stmt)
@@ -1287,7 +1298,11 @@ DefineRange(CreateRangeStmt *stmt)
Oid typeNamespace;
Oid typoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1304,6 +1319,8 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
+ Oid castFuncOid;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1452,8 +1469,10 @@ DefineRange(CreateRangeStmt *stmt)
/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for ranges */
alignment = (subtypalign == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
- /* Allocate OID for array type */
+ /* Allocate OID for array type, its multirange, and its multirange array */
rangeArrayOid = AssignTypeArrayOid();
+ multirangeOid = AssignTypeMultirangeOid();
+ multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
address =
@@ -1491,9 +1510,46 @@ DefineRange(CreateRangeStmt *stmt)
Assert(typoid == InvalidOid || typoid == address.objectId);
typoid = address.objectId;
+ /* Create the multirange that goes with it */
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(multirangeOid, /* force assignment of this type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_RANGE, /* type-category (range type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ Assert(multirangeOid == mltrngaddress.objectId);
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
@@ -1534,8 +1590,53 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ multirangeOid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ multirangeOid, typoid, rangeArrayOid,
+ &castFuncOid);
+
+ /* Create cast from the range type to its multirange type */
+ CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1613,6 +1714,147 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ *
+ * Sets castFuncOid to the oid of the new constructor that can be used
+ * to cast from a range to a multirange.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
+ Oid *castFuncOid)
+{
+ ObjectAddress myself,
+ referenced;
+ oidvector *argtypes;
+ Datum allParamTypes;
+ ArrayType *allParameterTypes;
+ Datum paramModes;
+ ArrayType *parameterModes;
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ /* 0-arg constructor - for empty multiranges */
+ argtypes = buildoidvector(NULL, 0);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor0", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /*
+ * Make the constructor internally-dependent on the multirange type so
+ * that they go away silently when the type is dropped. Note that pg_dump
+ * depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+
+ /*
+ * 1-arg constructor - for casts
+ *
+ * In theory we shouldn't need both this and the vararg (n-arg) constructor,
+ * but having a separate 1-arg function lets us define casts against it.
+ */
+ argtypes = buildoidvector(&rangeOid, 1);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor1", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ true, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ *castFuncOid = myself.objectId;
+
+ /* n-arg constructor - vararg */
+ argtypes = buildoidvector(&rangeArrayOid, 1);
+ allParamTypes = ObjectIdGetDatum(rangeArrayOid);
+ allParameterTypes = construct_array(&allParamTypes,
+ 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
+ parameterModes = construct_array(¶mModes, 1, CHAROID,
+ 1, true, 'c');
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor2", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(allParameterTypes), /* allParameterTypes */
+ PointerGetDatum(parameterModes), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ pfree(allParameterTypes);
+ pfree(parameterModes);
+}
/*
* Find suitable I/O functions for a type.
@@ -2045,6 +2287,72 @@ AssignTypeArrayOid(void)
return type_array_oid;
}
+/*
+ * AssignTypeMultirangeOid
+ *
+ * Pre-assign the range type's multirange OID for use in pg_type.oid
+ */
+Oid
+AssignTypeMultirangeOid(void)
+{
+ Oid type_multirange_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
+
+ type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
+ binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_oid;
+}
+
+/*
+ * AssignTypeMultirangeArrayOid
+ *
+ * Pre-assign the range type's multirange array OID for use in pg_type.typarray
+ */
+Oid
+AssignTypeMultirangeArrayOid(void)
+{
+ Oid type_multirange_array_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
+
+ type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
+ binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_array_oid;
+}
+
/*-------------------------------------------------------------------
* DefineCompositeType
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 6cab20d09a..66552e1078 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -188,6 +188,7 @@ coerce_type(ParseState *pstate, Node *node,
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID ||
targetTypeId == ANYCOMPATIBLEARRAYOID ||
targetTypeId == ANYCOMPATIBLERANGEOID)
{
@@ -195,12 +196,12 @@ coerce_type(ParseState *pstate, Node *node,
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call the pseudotype's input
- * function, which will produce an error. Also, if what we have is a
- * domain over array, enum, or range, we have to relabel it to its
- * base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * the pseudotype's input function, which will produce an error. Also,
+ * if what we have is a domain over array, enum, range, or multirange,
+ * we have to relabel it to its base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1531,8 +1532,8 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* 1) All arguments declared ANYELEMENT must have the same datatype.
* 2) All arguments declared ANYARRAY must have the same datatype,
* which must be a varlena array type.
- * 3) All arguments declared ANYRANGE must have the same datatype,
- * which must be a range type.
+ * 3) All arguments declared ANYRANGE or ANYMULTIRANGE must be a range or
+ * multirange type, all derived from the same base datatype.
* 4) If there are arguments of more than one of these polymorphic types,
* the array element type and/or range subtype must be the same as each
* other and the same as the ANYELEMENT type.
@@ -1582,8 +1583,10 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid anycompatible_range_typeid = InvalidOid;
Oid anycompatible_range_typelem = InvalidOid;
+ Oid range_typelem = InvalidOid;
bool have_anynonarray = false;
bool have_anyenum = false;
bool have_anycompatible_nonarray = false;
@@ -1632,6 +1635,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
else if (decl_type == ANYCOMPATIBLEOID ||
decl_type == ANYCOMPATIBLENONARRAYOID)
{
@@ -1722,8 +1734,6 @@ check_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- Oid range_typelem;
-
range_typelem = get_range_subtype(range_typeid);
if (!OidIsValid(range_typelem))
return false; /* should be a range, but isn't */
@@ -1742,6 +1752,45 @@ check_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ Oid multirange_typelem;
+
+ multirange_typelem = get_multirange_range(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+ }
+
if (have_anynonarray)
{
/* require the element type to not be an array or domain over array */
@@ -1820,8 +1869,10 @@ check_generic_type_consistency(const Oid *actual_arg_types,
* argument's actual type as the function's return type.
* 2) If return type is ANYARRAY, and any argument is ANYARRAY, use the
* argument's actual type as the function's return type.
- * 3) Similarly, if return type is ANYRANGE, and any argument is ANYRANGE,
- * use the argument's actual type as the function's return type.
+ * 3) Similarly, if return type is ANYRANGE or ANYMULTIRANGE, and any
+ * argument is ANYRANGE or ANYMULTIRANGE, use that argument's
+ * actual type, range type or multirange type as the function's return
+ * type.
* 4) Otherwise, if return type is ANYELEMENT or ANYARRAY, and there is
* at least one ANYELEMENT, ANYARRAY, or ANYRANGE input, deduce the
* return type from those inputs, or throw error if we can't.
@@ -1888,10 +1939,13 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid anycompatible_typeid = InvalidOid;
Oid anycompatible_array_typeid = InvalidOid;
Oid anycompatible_range_typeid = InvalidOid;
Oid anycompatible_range_typelem = InvalidOid;
+ Oid range_typelem;
+ Oid multirange_typelem;
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
bool have_anycompatible_nonarray = (rettype == ANYCOMPATIBLENONARRAYOID);
@@ -1976,6 +2030,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ n_poly_args++;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_poly_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
else if (decl_type == ANYCOMPATIBLEOID ||
decl_type == ANYCOMPATIBLENONARRAYOID)
{
@@ -2112,8 +2186,6 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- Oid range_typelem;
-
range_typelem = get_range_subtype(range_typeid);
if (!OidIsValid(range_typelem))
ereport(ERROR,
@@ -2142,6 +2214,61 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(elem_typeid))));
}
}
+ else
+ range_typelem = InvalidOid;
+
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_multirange_range(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * if we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
+ }
+ else
+ multirange_typelem = InvalidOid;
if (!OidIsValid(elem_typeid))
{
@@ -2150,6 +2277,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -2330,6 +2458,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -2366,6 +2505,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYCOMPATIBLE use the appropriate type */
if (rettype == ANYCOMPATIBLEOID ||
rettype == ANYCOMPATIBLENONARRAYOID)
@@ -2417,20 +2572,36 @@ check_valid_polymorphic_signature(Oid ret_type,
const Oid *declared_arg_types,
int nargs)
{
- if (ret_type == ANYRANGEOID || ret_type == ANYCOMPATIBLERANGEOID)
+ if (ret_type == ANYRANGEOID || ret_type == ANYMULTIRANGEOID)
{
/*
- * ANYRANGE requires an ANYRANGE input, else we can't tell which of
- * several range types with the same element type to use. Likewise
- * for ANYCOMPATIBLERANGE.
+ * ANYRANGE and ANYMULTIRANGE require an ANYRANGE or ANYMULTIRANGE input,
+ * else we can't tell which of several range types with the same element
+ * type to use.
+ */
+ for (int i = 0; i < nargs; i++)
+ {
+ if (declared_arg_types[i] == ANYRANGEOID ||
+ declared_arg_types[i] == ANYMULTIRANGEOID)
+ return NULL; /* OK */
+ }
+ return psprintf(_("A result of type %s requires at least one input of type anyrange or anymultirange."),
+ format_type_be(ret_type));
+ }
+ else if (ret_type == ANYCOMPATIBLERANGEOID)
+ {
+ /*
+ * ANYCOMPATIBLERANGE requires an ANYCOMPATIBLERANGE input,
+ * else we can't tell which of several range types with the same element
+ * type to use.
*/
for (int i = 0; i < nargs; i++)
{
if (declared_arg_types[i] == ret_type)
return NULL; /* OK */
}
- return psprintf(_("A result of type %s requires at least one input of type %s."),
- format_type_be(ret_type), format_type_be(ret_type));
+ return psprintf(_("A result of type %s requires at least one input of type anycompatiblerange."),
+ format_type_be(ret_type));
}
else if (IsPolymorphicTypeFamily1(ret_type))
{
@@ -2441,7 +2612,7 @@ check_valid_polymorphic_signature(Oid ret_type,
return NULL; /* OK */
}
/* Keep this list in sync with IsPolymorphicTypeFamily1! */
- return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange."),
+ return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange."),
format_type_be(ret_type));
}
else if (IsPolymorphicTypeFamily2(ret_type))
@@ -2593,6 +2764,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..7118dd0034 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..da78227b01
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,2181 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ FmgrInfo typioproc; /* range type's I/O proc */
+ Oid typioparam; /* range type's I/O parameter */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData *get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks with either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str = NULL;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 1;
+ range_str_copy = pnstrdup(range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **)
+ repalloc(ranges, range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(InputFunctionCall(&cache->typioproc,
+ range_str_copy,
+ cache->typioparam,
+ typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->typioproc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: First a int32-sized count of ranges, followed by
+ * ranges in their native binary representation.
+ */
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ StringInfoData tmpbuf;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc(range_count * sizeof(RangeType *));
+
+ initStringInfo(&tmpbuf);
+ for (int i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+
+ resetStringInfo(&tmpbuf);
+ appendBinaryStringInfo(&tmpbuf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(ReceiveFunctionCall(&cache->typioproc,
+ &tmpbuf,
+ cache->typioparam,
+ typmod));
+ }
+ pfree(tmpbuf.data);
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, cache->typcache->rngtype,
+ range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (int i = 0; i < range_count; i++)
+ {
+ Datum range;
+
+ range = RangeTypePGetDatum(ranges[i]);
+ range = PointerGetDatum(SendFunctionCall(&cache->typioproc, range));
+
+ pq_sendint32(buf, VARSIZE(range) - VARHDRSZ);
+ pq_sendbytes(buf, VARDATA(range), VARSIZE(range) - VARHDRSZ);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ Oid typiofunc;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &typiofunc);
+
+ if (!OidIsValid(typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(typiofunc, &cache->typioproc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ bytelen += MAXALIGN(VARSIZE(ranges[i]));
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ memcpy(ptr, ranges[i], VARSIZE(ranges[i]));
+ ptr += MAXALIGN(VARSIZE(ranges[i]));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of arbitrary ranges into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ *
+ * Returns the number of slots actually used, which may be less than
+ * input_range_count but never more.
+ *
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType *multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor2(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_CARDINALITY_VIOLATION),
+ errmsg("multiranges cannot be constructed from multi-dimensional arrays")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Construct multirange value from a single range.
+ * It'd be nice if we could just use multirange_constructor2
+ * for this case, but we need a non-variadic single-arg function
+ * to let us define a CAST from a range to its multirange.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ RangeType *range;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ range = PG_GETARG_RANGE_P(0);
+
+ /* Make sure the range type matches. */
+ rngtypid = RangeTypeGetOid(range);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 1, &range));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ elog(ERROR, /* can't happen */
+ "niladic multirange constructor must not receive arguments");
+}
+
+
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+multirange_union(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
+}
+
+/* multirange minus */
+Datum
+multirange_minus(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+multirange_intersect(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+ /*-----------------------------------------------
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1,
+ * but one extra won't hurt.
+ *-----------------------------------------------
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType *mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges1[range_count1 - 1],
+ ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype,
+ ranges1[0],
+ ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1, MultirangeType *mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
+ return false;
+
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
+ return false;
+ }
+
+ /* All ranges in mr2 are satisfied */
+ return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 18f2ee8226..248ea7f44a 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -51,6 +51,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
+Datum
+binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
+binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_array_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
Datum
binary_upgrade_set_next_toast_pg_type_oid(PG_FUNCTION_ARGS)
{
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 3d6b2f9093..4fcd8ce6de 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -227,6 +228,30 @@ anycompatiblerange_out(PG_FUNCTION_ARGS)
return range_out(fcinfo);
}
+/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
/*
* void
*
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 01ad8bc240..0af3c67296 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -431,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -452,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -475,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -485,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -495,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -505,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -515,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -957,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -969,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -993,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1101,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1110,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1132,9 +1180,81 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
}
+/* range, range -> range, range functions */
+
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
+ {
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
+ }
+
+ return false;
+}
+
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
+}
+
+
/* Btree support */
/* btree comparator */
@@ -1144,12 +1264,6 @@ range_cmp(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
- RangeBound lower1,
- lower2;
- RangeBound upper1,
- upper2;
- bool empty1,
- empty2;
int cmp;
check_stack_depth(); /* recurses when subtype is a range type */
@@ -1160,6 +1274,28 @@ range_cmp(PG_FUNCTION_ARGS)
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+ cmp = range_cmp_internal(typcache, r1, r2);
+
+ PG_FREE_IF_COPY(r1, 0);
+ PG_FREE_IF_COPY(r2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
@@ -1177,10 +1313,7 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
- PG_FREE_IF_COPY(r1, 0);
- PG_FREE_IF_COPY(r2, 1);
-
- PG_RETURN_INT32(cmp);
+ return cmp;
}
/* inequality operators using the range_cmp function */
@@ -1218,13 +1351,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1363,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1404,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1433,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1471,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1769,6 +1916,21 @@ range_get_flags(const RangeType *range)
return *((char *) range + VARSIZE(range) - 1);
}
+/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
@@ -1937,6 +2099,46 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
b1->val, b2->val));
}
+/*
+ * qsort callback for sorting ranges.
+ *
+ * Two empty ranges compare equal; an empty range sorts to the left of any
+ * non-empty range. Two non-empty ranges are sorted by lower bound first
+ * and by upper bound next.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
/*
* Build an empty range value of the type indicated by the typcache entry.
*/
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 0a6db0d478..6e9a48b314 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2529,6 +2529,16 @@ type_is_range(Oid typid)
return (get_typtype(typid) == TYPTYPE_RANGE);
}
+/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
/*
* get_type_category_preferred
*
@@ -3184,7 +3194,7 @@ get_namespace_name_or_temp(Oid nspid)
return get_namespace_name(nspid);
}
-/* ---------- PG_RANGE CACHE ---------- */
+/* ---------- PG_RANGE CACHES ---------- */
/*
* get_range_subtype
@@ -3237,6 +3247,56 @@ get_range_collation(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngmultitypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_multirange_range
+ * Returns the range type of a given multirange
+ *
+ * Returns InvalidOid if the type is not a multirange.
+ */
+Oid
+get_multirange_range(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGEMULTIRANGE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 53d9ddf159..7654331a59 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -651,6 +651,18 @@ static const struct cachedesc cacheinfo[] = {
},
64
},
+ {RangeRelationId, /* RANGEMULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngmultitypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+
{RangeRelationId, /* RANGETYPE */
RangeTypidIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 854f133f9b..f1d3cb97d0 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -286,6 +286,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -302,6 +303,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -553,8 +557,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -591,7 +595,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -616,7 +620,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -641,7 +645,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -693,6 +697,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -737,6 +748,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -816,6 +834,16 @@ lookup_type_cache(Oid type_id, int flags)
(void) lookup_type_cache(typentry->rngelemtype->type_id, 0);
}
+ /*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
/*
* If requested, get information about a domain type
*/
@@ -927,6 +955,22 @@ load_rangetype_info(TypeCacheEntry *typentry)
typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
}
+/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Oid rangetypeOid;
+
+ rangetypeOid = get_multirange_range(typentry->type_id);
+ if (!OidIsValid(rangetypeOid))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
/*
* load_domaintype_info --- helper routine to set up domain constraint info
@@ -1527,11 +1571,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1574,6 +1618,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 78ed857203..ce09d0d128 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -35,6 +35,7 @@ typedef struct polymorphic_actuals
Oid anyelement_type; /* anyelement mapping, if known */
Oid anyarray_type; /* anyarray mapping, if known */
Oid anyrange_type; /* anyrange mapping, if known */
+ Oid anymultirange_type; /* anymultirange mapping, if known */
} polymorphic_actuals;
static void shutdown_MultiFuncCall(Datum arg);
@@ -46,6 +47,7 @@ static TypeFuncClass internal_get_result_type(Oid funcid,
static void resolve_anyelement_from_others(polymorphic_actuals *actuals);
static void resolve_anyarray_from_others(polymorphic_actuals *actuals);
static void resolve_anyrange_from_others(polymorphic_actuals *actuals);
+static void resolve_anymultirange_from_others(polymorphic_actuals *actuals);
static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
oidvector *declared_args,
Node *call_expr);
@@ -503,6 +505,34 @@ resolve_anyelement_from_others(polymorphic_actuals *actuals)
format_type_be(range_base_type))));
actuals->anyelement_type = range_typelem;
}
+ else if (OidIsValid(actuals->anymultirange_type))
+ {
+ /* Use the element type based on the multirange type */
+ Oid multirange_base_type;
+ Oid multirange_typelem;
+ Oid range_base_type;
+ Oid range_typelem;
+
+ multirange_base_type = getBaseType(actuals->anymultirange_type);
+ multirange_typelem = get_multirange_range(multirange_base_type);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_base_type))));
+
+ range_base_type = getBaseType(multirange_typelem);
+ range_typelem = get_range_subtype(range_base_type);
+
+ if (!OidIsValid(range_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s does not contain a range type but type %s",
+ "anymultirange",
+ format_type_be(range_base_type))));
+ actuals->anyelement_type = range_typelem;
+ }
else
elog(ERROR, "could not determine polymorphic type");
}
@@ -540,10 +570,54 @@ static void
resolve_anyrange_from_others(polymorphic_actuals *actuals)
{
/*
- * We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types with the same subtype.
+ * We can't deduce a range type from other polymorphic array or base types,
+ * because there may be multiple range types with the same subtype, but we
+ * can deduce it from a polymorphic multirange type.
*/
- elog(ERROR, "could not determine polymorphic type");
+ if (OidIsValid(actuals->anymultirange_type))
+ {
+ /* Use the element type based on the multirange type */
+ Oid multirange_base_type = getBaseType(actuals->anymultirange_type);
+ Oid multirange_typelem =
+ get_multirange_range(multirange_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_base_type))));
+ actuals->anyrange_type = multirange_typelem;
+ }
+ else
+ elog(ERROR, "could not determine polymorphic type");
+}
+
+/*
+ * Resolve actual type of ANYMULTIRANGE from other polymorphic inputs
+ */
+static void
+resolve_anymultirange_from_others(polymorphic_actuals *actuals)
+{
+ /*
+ * We can't deduce a multirange type from polymorphic array or base types,
+ * because there may be multiple range types with the same subtype, but we
+ * can deduce it from a polymorphic range type.
+ */
+ if (OidIsValid(actuals->anyrange_type))
+ {
+ Oid range_base_type = getBaseType(actuals->anyrange_type);
+ Oid multirange_typeid = get_range_multirange(range_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(actuals->anyrange_type))));
+ actuals->anymultirange_type = multirange_typeid;
+ }
+ else
+ elog(ERROR, "could not determine polymorphic type");
}
/*
@@ -566,6 +640,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anycompatible_result = false;
bool have_anycompatible_array_result = false;
bool have_anycompatible_range_result = false;
@@ -594,6 +669,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
have_polymorphic_result = true;
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_polymorphic_result = true;
+ have_anymultirange_result = true;
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
have_polymorphic_result = true;
@@ -660,6 +739,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
}
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ {
+ poly_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ return false;
+ }
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
if (!OidIsValid(anyc_actuals.anyelement_type))
@@ -703,6 +791,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&poly_actuals);
+
if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
resolve_anyelement_from_others(&anyc_actuals);
@@ -780,6 +871,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ poly_actuals.anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
TupleDescInitEntry(tupdesc, i + 1,
@@ -834,6 +933,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anycompatible_result = false;
bool have_anycompatible_array_result = false;
bool have_anycompatible_range_result = false;
@@ -912,6 +1012,24 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = poly_actuals.anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
+ have_anymultirange_result = true;
+ }
+ else
+ {
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ {
+ poly_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ return false;
+ }
+ argtypes[i] = poly_actuals.anymultirange_type;
+ }
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
@@ -988,6 +1106,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&poly_actuals);
+
if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
resolve_anyelement_from_others(&anyc_actuals);
@@ -1013,6 +1134,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = poly_actuals.anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = poly_actuals.anymultirange_type;
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
argtypes[i] = anyc_actuals.anyelement_type;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 408637cfec..cf4ce0a5a6 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -276,7 +276,8 @@ static void dumpSearchPath(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type);
+ bool force_array_type,
+ bool include_multirange_type);
static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
static void binary_upgrade_set_pg_class_oids(Archive *fout,
@@ -1648,7 +1649,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4416,16 +4417,49 @@ append_depends_on_extension(Archive *fout,
}
}
+static Oid
+get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+{
+ /*
+ * If the old version didn't assign an array type, but the new version
+ * does, we must select an unused type OID to assign. This currently only
+ * happens for domains, when upgrading pre-v11 to v11 and up.
+ *
+ * Note: local state here is kind of ugly, but we must have some, since we
+ * mustn't choose the same unused OID more than once.
+ */
+ static Oid next_possible_free_oid = FirstNormalObjectId;
+ PGresult *res;
+ bool is_dup;
+
+ do
+ {
+ ++next_possible_free_oid;
+ printfPQExpBuffer(upgrade_query,
+ "SELECT EXISTS(SELECT 1 "
+ "FROM pg_catalog.pg_type "
+ "WHERE oid = '%u'::pg_catalog.oid);",
+ next_possible_free_oid);
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+ is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
+ PQclear(res);
+ } while (is_dup);
+
+ return next_possible_free_oid;
+}
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type)
+ bool force_array_type,
+ bool include_multirange_type)
{
PQExpBuffer upgrade_query = createPQExpBuffer();
PGresult *res;
Oid pg_type_array_oid;
+ Oid pg_type_multirange_oid;
+ Oid pg_type_multirange_array_oid;
appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
@@ -4446,33 +4480,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQclear(res);
if (!OidIsValid(pg_type_array_oid) && force_array_type)
- {
- /*
- * If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
- *
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
- */
- static Oid next_possible_free_oid = FirstNormalObjectId;
- bool is_dup;
-
- do
- {
- ++next_possible_free_oid;
- printfPQExpBuffer(upgrade_query,
- "SELECT EXISTS(SELECT 1 "
- "FROM pg_catalog.pg_type "
- "WHERE oid = '%u'::pg_catalog.oid);",
- next_possible_free_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
- is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
- PQclear(res);
- } while (is_dup);
-
- pg_type_array_oid = next_possible_free_oid;
- }
+ pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
if (OidIsValid(pg_type_array_oid))
{
@@ -4483,6 +4491,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
pg_type_array_oid);
}
+ /*
+ * Pre-set the multirange type oid and its own array type oid.
+ */
+ if (include_multirange_type)
+ {
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBuffer(upgrade_query,
+ "SELECT t.oid, t.typarray "
+ "FROM pg_catalog.pg_type t "
+ "JOIN pg_catalog.pg_range r "
+ "ON t.oid = r.rngmultitypid "
+ "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
+ pg_type_oid);
+
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+
+ pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+ pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
+
+ PQclear(res);
+ }
+ else
+ {
+ pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ }
+
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_oid);
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_array_oid);
+ }
+
destroyPQExpBuffer(upgrade_query);
}
@@ -4516,7 +4564,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
- pg_type_oid, false);
+ pg_type_oid, false, false);
if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
{
@@ -5065,6 +5113,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
@@ -8203,9 +8256,12 @@ getProcLangs(Archive *fout, int *numProcLangs)
/*
* getCasts
- * get basic information about every cast in the system
+ * get basic information about most casts in the system
*
* numCasts is set to the number of casts read in
+ *
+ * Skip casts from a range to its multirange, since we'll create those
+ * automatically.
*/
CastInfo *
getCasts(Archive *fout, int *numCasts)
@@ -8223,7 +8279,20 @@ getCasts(Archive *fout, int *numCasts)
int i_castcontext;
int i_castmethod;
- if (fout->remoteVersion >= 80400)
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBufferStr(query, "SELECT tableoid, oid, "
+ "castsource, casttarget, castfunc, castcontext, "
+ "castmethod "
+ "FROM pg_cast c "
+ "WHERE NOT EXISTS ( "
+ "SELECT 1 FROM pg_range r "
+ "WHERE c.castsource = r.rngtypid "
+ "AND c.casttarget = r.rngmultitypid "
+ ") "
+ "ORDER BY 3,4");
+ }
+ else if (fout->remoteVersion >= 80400)
{
appendPQExpBufferStr(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
@@ -10411,7 +10480,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
qualtypname);
@@ -10537,7 +10606,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, true);
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
qualtypname);
@@ -10643,7 +10712,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
qualtypname);
@@ -10848,7 +10917,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
@@ -11035,7 +11104,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- true); /* force array type */
+ true, /* force array type */
+ false); /* force multirange type */
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -11223,7 +11293,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
{
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
}
@@ -11497,7 +11567,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
stinfo->baseType->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
fmtQualifiedDumpable(stinfo));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 3e11166615..0b717412b4 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -176,6 +176,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 70157cf90a..c262265b95 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -139,8 +139,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 12d94fe1b3..01d95117cd 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,6 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 8be303870f..566f9364c5 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -327,6 +327,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_rngmultitypid_index, 8001, on pg_range using btree(rngmultitypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index ffabe275c0..03bab74253 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..827ac4f03c 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index cef63b2a71..5ac1c478cb 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -285,6 +285,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
@@ -445,6 +447,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
@@ -1266,12 +1273,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index 01c5328ddd..f555af90ac 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -526,4 +526,17 @@
{ castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
castcontext => 'e', castmethod => 'f' },
+# range to multirange
+{ castsource => 'int4range', casttarget => 'int4multirange', castfunc => 'int4multirange(int4range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'int8range', casttarget => 'int8multirange', castfunc => 'int8multirange(int8range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'numrange', casttarget => 'nummultirange', castfunc => 'nummultirange(numrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'daterange', casttarget => 'datemultirange', castfunc => 'datemultirange(daterange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tsrange', casttarget => 'tsmultirange', castfunc => 'tsmultirange(tsrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tstzrange', casttarget => 'tstzmultirange', castfunc => 'tstzmultirange(tstzrange)',
+ castcontext => 'e', castmethod => 'f' },
]
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index ab2f50c9eb..eebebbcdb4 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 65c7fedf23..08cda3ba36 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3302,5 +3302,174 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'matchingsel', oprjoin => 'matchingjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 26227df216..901fd7b303 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a649e44d08..d6afad645b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9718,6 +9718,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9767,6 +9771,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9835,6 +9846,258 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8114',
+ proname => 'multirange_union', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union' },
+{ oid => '8120',
+ proname => 'multirange_minus', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus' },
+{ oid => '8126',
+ proname => 'multirange_intersect', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8146', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 't',
+ prorettype => 'int4multirange', proargtypes => 'int4range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8147', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 't',
+ prorettype => 'nummultirange', proargtypes => 'numrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8148', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 't',
+ prorettype => 'tsmultirange', proargtypes => 'tsrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8149', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 't',
+ prorettype => 'tstzmultirange', proargtypes => 'tstzrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8150', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 't',
+ prorettype => 'datemultirange', proargtypes => 'daterange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8151', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 't',
+ prorettype => 'int8multirange', proargtypes => 'int8range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8152', descr => 'anymultirange cast',
+ proname => 'multirange', proisstrict => 't',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
@@ -10217,6 +10480,14 @@
proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
+{ oid => '8144', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
+{ oid => '8145', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
{ oid => '3585', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index 479754c245..10060255c9 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ rngmultitypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', rngmultitypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', rngmultitypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', rngmultitypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ rngmultitypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ rngmultitypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index 98172bb1b6..60a8fde518 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -34,6 +34,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* OID of range's element type (subtype) */
Oid rngsubtype BKI_LOOKUP(pg_type);
+ /* OID of the range's multirange type */
+ Oid rngmultitypid BKI_LOOKUP(pg_type);
+
/* collation for this range type, or 0 */
Oid rngcollation BKI_DEFAULT(0);
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 2e6110e3f2..cb3d159bf1 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -491,6 +491,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -620,5 +654,10 @@
typtype => 'p', typcategory => 'P', typinput => 'anycompatiblerange_in',
typoutput => 'anycompatiblerange_out', typreceive => '-', typsend => '-',
typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 7b37562648..f1818d9a52 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -263,6 +263,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -304,7 +305,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#define IsPolymorphicTypeFamily2(typid) \
((typid) == ANYCOMPATIBLEOID || \
@@ -369,4 +371,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 23130895af..989914dfe1 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
+extern Oid AssignTypeMultirangeOid(void);
+extern Oid AssignTypeMultirangeArrayOid(void);
extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 374f57fb43..f2e73eabb0 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -156,6 +156,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -182,6 +183,8 @@ extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
extern Oid get_range_collation(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_multirange_range(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
extern bool get_index_isreplident(Oid index_oid);
extern bool get_index_isvalid(Oid index_oid);
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..b270eb7f51
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the count are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern MultirangeType *multirange_minus_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType *multirange_intersect_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType *range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType *make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType *make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 0cbbf09033..204aee4054 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
@@ -92,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -115,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -125,6 +141,7 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
@@ -132,8 +149,12 @@ extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f27b73d76d..e8f393520a 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -79,6 +79,7 @@ enum SysCacheIdentifier
PUBLICATIONOID,
PUBLICATIONREL,
PUBLICATIONRELMAP,
+ RANGEMULTIRANGE,
RANGETYPE,
RELNAMENSP,
RELOID,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index cdd20e56d7..33f332a141 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -100,6 +100,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_canonical_finfo; /* canonicalization function, if any */
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
+ /*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype; /* multirange's range underlying type */
+
/*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
@@ -143,6 +148,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 828ff5a288..1e3ff02289 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -513,6 +513,8 @@ do_compile(FunctionCallInfo fcinfo,
else if (rettypeid == ANYRANGEOID ||
rettypeid == ANYCOMPATIBLERANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY or ANYCOMPATIBLE */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2089,6 +2091,7 @@ build_datatype(HeapTuple typeTup, int32 typmod,
case TYPTYPE_BASE:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
+ case TYPTYPE_MULTIRANGE:
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_COMPOSITE:
@@ -2507,6 +2510,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYCOMPATIBLERANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..8232795148 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -140,6 +140,7 @@ owner of sequence deptest_a_seq
owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
+owner of type deptest_multirange
owner of type deptest_range
owner of table deptest2
owner of sequence ss1
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..23a06b301f
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,2395 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+ int4multirange
+----------------
+ {}
+(1 row)
+
+select int4range(1, 3)::int4multirange;
+ int4range
+-----------
+ {[1,3)}
+(1 row)
+
+select int4range(1, null)::int4multirange;
+ int4range
+-----------
+ {[1,)}
+(1 row)
+
+select int4range(null, null)::int4multirange;
+ int4range
+-----------
+ {(,)}
+(1 row)
+
+select 'empty'::textrange::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textrange('a', 'c')::textmultirange;
+ textrange
+-----------
+ {[a,c)}
+(1 row)
+
+select textrange('a', null)::textmultirange;
+ textrange
+-----------
+ {[a,)}
+(1 row)
+
+select textrange(null, null)::textmultirange;
+ textrange
+-----------
+ {(,)}
+(1 row)
+
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT nummultirange() + nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT nummultirange() - nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() * nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+ mr_polymorphic
+----------------
+ {[1,4)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 2efd7d7ec7..c02443ee40 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -206,6 +213,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -224,6 +233,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -335,7 +346,8 @@ WHERE p1.prorettype IN
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+----------------
@@ -350,20 +362,25 @@ ORDER BY 2;
3532 | enum_recv
(9 rows)
--- anyrange is tighter than the rest, can only resolve from anyrange input
+-- anyrange and anymultirange are tighter than the rest, can only resolve
+-- from each other
SELECT p1.oid, p1.proname
FROM pg_proc as p1
-WHERE p1.prorettype = 'anyrange'::regtype
+WHERE p1.prorettype IN ('anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
- 'anyrange'::regtype = ANY (p1.proargtypes)
+ ('anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
+ 8002 | anymultirange_in
3832 | anyrange_in
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(4 rows)
+(7 rows)
-- similarly for the anycompatible family
SELECT p1.oid, p1.proname
@@ -2181,13 +2198,14 @@ ORDER BY 1, 2, 3;
| float_ops | float4_ops | real
| float_ops | float8_ops | double precision
| jsonb_ops | jsonb_ops | jsonb
+ | multirange_ops | multirange_ops | anymultirange
| numeric_ops | numeric_ops | numeric
| range_ops | range_ops | anyrange
| record_image_ops | record_image_ops | record
| record_ops | record_ops | record
| tsquery_ops | tsquery_ops | tsquery
| tsvector_ops | tsvector_ops | tsvector
-(14 rows)
+(15 rows)
-- **************** pg_index ****************
-- Look for illegal values in pg_index fields.
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index d0a6b630b8..65827c03e6 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -1811,7 +1811,7 @@ begin
return array[x + 1, x + 2];
end$$ language plpgsql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function f1(x anyrange) returns anyarray as $$
begin
return array[lower(x), upper(x)];
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index 1ff40764d9..3a42b30730 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -61,7 +61,7 @@ create function polyf(x anyelement) returns anyrange as $$
select array[x + 1, x + 2]
$$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function polyf(x anyrange) returns anyarray as $$
select array[lower(x), upper(x)]
$$ language sql;
@@ -231,7 +231,7 @@ CREATE AGGREGATE myaggp01a(*) (SFUNC = stfnp, STYPE = int4[],
CREATE AGGREGATE myaggp02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- N P
-- should CREATE
CREATE AGGREGATE myaggp03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -243,11 +243,11 @@ CREATE AGGREGATE myaggp03b(*) (SFUNC = stfp, STYPE = int4[],
CREATE AGGREGATE myaggp04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp04b(*) (SFUNC = stfp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- Case2 (R = P) && ((B = P) || (B = N))
-- -------------------------------------
-- S tf1 B tf2
@@ -302,13 +302,13 @@ ERROR: function tfp(integer[], anyelement) does not exist
CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N N P
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N P N
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -324,21 +324,21 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P N P
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P P N
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p,
@@ -368,11 +368,11 @@ CREATE AGGREGATE myaggn01b(*) (SFUNC = stfnp, STYPE = int4[],
CREATE AGGREGATE myaggn02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn02b(*) (SFUNC = stfnp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- N P
-- should CREATE
CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -382,7 +382,7 @@ CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
CREATE AGGREGATE myaggn04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- Case4 (R = N) && ((B = P) || (B = N))
-- -------------------------------------
-- S tf1 B tf2
@@ -436,21 +436,21 @@ ERROR: function tfp(integer[], anyelement) does not exist
CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N N P
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N P N
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -472,13 +472,13 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P N P
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P P N
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p,
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 7eced28452..71e2769f44 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -1556,7 +1556,7 @@ DROP FUNCTION dup(anyelement);
CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE FUNCTION dup (f1 anycompatible, f2 anycompatiblearray, f3 out anycompatible, f4 out anycompatiblearray)
AS 'select $1, $2' LANGUAGE sql;
SELECT dup(22, array[44]);
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index c421f5394f..763e3819a5 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1371,12 +1389,12 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function range_add_bounds(anyrange)
returns anyelement as 'select lower($1) + upper($1)' language sql;
select range_add_bounds(int4range(1, 17));
@@ -1508,6 +1526,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1525,6 +1544,16 @@ select * from outparam2_succeed(int4range(1,11));
{1,11} | {11,1}
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1547,14 +1576,14 @@ select * from table_succeed(int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 192445878d..d9ce961be2 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
numrange_test|t
onek|t
onek2|t
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 274130e706..8df1ae47e4 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -195,18 +195,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -240,17 +241,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -318,18 +320,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -363,17 +366,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -631,3 +635,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
+ rngtypid | rngsubtype | rngmultitypid
+----------+------------+---------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index a98dba7b2f..18c25f4d8b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 3f66e0b859..dca9f91afb 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..0f70919e1a
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,630 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+select int4range(1, 3)::int4multirange;
+select int4range(1, null)::int4multirange;
+select int4range(null, null)::int4multirange;
+select 'empty'::textrange::textmultirange;
+select textrange('a', 'c')::textmultirange;
+select textrange('a', null)::textmultirange;
+select textrange(null, null)::textmultirange;
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT nummultirange() + nummultirange();
+SELECT nummultirange() + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT nummultirange() - nummultirange();
+SELECT nummultirange() - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+SELECT nummultirange() * nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 9a1ea3d999..69c4859102 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -279,16 +290,19 @@ WHERE p1.prorettype IN
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
--- anyrange is tighter than the rest, can only resolve from anyrange input
+-- anyrange and anymultirange are tighter than the rest, can only resolve
+-- from each other
SELECT p1.oid, p1.proname
FROM pg_proc as p1
-WHERE p1.prorettype = 'anyrange'::regtype
+WHERE p1.prorettype IN ('anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
- 'anyrange'::regtype = ANY (p1.proargtypes)
+ ('anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- similarly for the anycompatible family
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 4048b1d185..0276d19136 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -528,6 +532,7 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
@@ -539,6 +544,13 @@ create function outparam2_succeed(r anyrange, out lu anyarray, out ul anyarray)
select * from outparam2_succeed(int4range(1,11));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index 4b492ce062..07879d9a57 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -153,7 +153,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -182,7 +182,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -234,7 +234,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -263,7 +263,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -467,3 +467,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 34623523a7..a2481e3837 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1362,6 +1362,9 @@ MultiXactMember
MultiXactOffset
MultiXactStateData
MultiXactStatus
+MultirangeIOData
+MultirangeParseState
+MultirangeType
MyData
NDBOX
NODE
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-14 18:13 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-17 04:52 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-19 20:41 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-19 20:43 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-20 05:24 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-23 23:32 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-24 04:23 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2020-04-04 23:10 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-04-11 03:44 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-04-11 16:36 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
1 sibling, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2020-04-11 03:44 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
On Sat, Apr 4, 2020 at 4:10 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
> Sorry I didn't reply earlier, but I didn't know the answer then and I
> still don't know the answer now.
Okay, thanks Alvaro! I'll see if I can figure it out myself. I assume
it is actually possible, right? I've seen references to on-disk format
vs in-memory format before, but I've never encountered anything in the
code supporting a difference.
> Anyway, I rebased this to verify that the code hasn't broken, and it
> hasn't -- the tests still pass. There was a minor conflict in
> pg_operator.dat which I fixed.
Thanks, and thanks for your v17 also. Here is a patch building on that
and adding support for anycompatiblemultirange.
Regards,
Paul
Attachments:
[application/octet-stream] v18-multirange.patch (364.2K, ../../CA+renyUPu5yVzw_2mQ=+nNxwRVP_fnjMYaN591Fdw5kMKHQV2Q@mail.gmail.com/2-v18-multirange.patch)
download | inline diff:
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index c2e42f31c0..22404d18cd 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4812,6 +4812,10 @@ SELECT * FROM pg_attribute
</indexterm>
<indexterm zone="datatype-pseudo">
+ <primary>anymultirange</primary>
+ </indexterm>
+
+ <indexterm zone="datatype-pseudo">
<primary>anycompatible</primary>
</indexterm>
@@ -4828,6 +4832,10 @@ SELECT * FROM pg_attribute
</indexterm>
<indexterm zone="datatype-pseudo">
+ <primary>anycompatiblemultirange</primary>
+ </indexterm>
+
+ <indexterm zone="datatype-pseudo">
<primary>void</primary>
</indexterm>
@@ -4937,6 +4945,13 @@ SELECT * FROM pg_attribute
</row>
<row>
+ <entry><type>anymultirange</type></entry>
+ <entry>Indicates that a function accepts any multirange data type
+ (see <xref linkend="extend-types-polymorphic"/> and
+ <xref linkend="rangetypes"/>).</entry>
+ </row>
+
+ <row>
<entry><type>anycompatible</type></entry>
<entry>Indicates that a function accepts any data type,
with automatic promotion of multiple arguments to a common data type
@@ -4966,6 +4981,14 @@ SELECT * FROM pg_attribute
</row>
<row>
+ <entry><type>anycompatiblemultirange</type></entry>
+ <entry>Indicates that a function accepts any multirange data type,
+ with automatic promotion of multiple arguments to a common data type
+ (see <xref linkend="extend-types-polymorphic"/> and
+ <xref linkend="rangetypes"/>).</entry>
+ </row>
+
+ <row>
<entry><type>cstring</type></entry>
<entry>Indicates that a function accepts or returns a null-terminated C string.</entry>
</row>
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 930aeb767c..3f62c06daf 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -286,6 +286,14 @@
</row>
<row>
+ <entry><type>anymultirange</type></entry>
+ <entry>Simple</entry>
+ <entry>Indicates that a function accepts any multirange data type
+ (see <xref linkend="rangetypes"/>)
+ </entry>
+ </row>
+
+ <row>
<entry><type>anycompatible</type></entry>
<entry>Common</entry>
<entry>Indicates that a function accepts any data type,
@@ -316,6 +324,14 @@
with automatic promotion of multiple arguments to a common data type
</entry>
</row>
+
+ <row>
+ <entry><type>anycompatiblemultirange</type></entry>
+ <entry>Common</entry>
+ <entry>Indicates that a function accepts any multirange data type,
+ with automatic promotion of multiple arguments to a common data type
+ </entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -343,17 +359,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type> or <type>anyarray</type>,
- the actual range type in the <type>anyrange</type> positions must be a
- range whose subtype is the same type appearing in
- the <type>anyelement</type> positions and the same as the element type
- of the <type>anyarray</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -363,6 +377,19 @@
</para>
<para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type> or <type>anyarray</type>,
+ the actual range type in the <type>anyrange</type> positions must be a
+ range whose subtype is the same type appearing in
+ the <type>anyelement</type> positions and the same as the element type
+ of the <type>anyarray</type> positions.
+ If there are positions declared <type>anymultirange</type>,
+ their actual multirange type must contain ranges matching parameters declared
+ <type>anyrange</type> and base elements matching parameters declared
+ <type>anyelement</type> and <type>anyarray</type>.
+ </para>
+
+ <para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
types are allowed. For example, a function declared as
@@ -417,7 +444,8 @@
Selection of the common type considers the actual types
of <type>anycompatible</type> and <type>anycompatiblenonarray</type>
inputs, the array element types of <type>anycompatiblearray</type>
- inputs, and the range subtypes of <type>anycompatiblerange</type>
+ inputs, the range subtypes of <type>anycompatiblerange</type> inputs,
+ and the multirange subtypes of <type>anycompatiablemultirange</type>
inputs. If <type>anycompatiblenonarray</type> is present then the
common type is required to be a non-array type. Once a common type is
identified, arguments in <type>anycompatible</type>
@@ -428,12 +456,15 @@
<para>
Since there is no way to select a range type knowing only its subtype,
- use of <type>anycompatiblerange</type> requires that all arguments
- declared with that type have the same actual range type, and that that
- type's subtype agree with the selected common type, so that no casting
- of the range values is required. As with <type>anyrange</type>, use
- of <type>anycompatiblerange</type> as a function result type requires
- that there be an <type>anycompatiblerange</type> argument.
+ use of <type>anycompatiblerange</type> and/or
+ <type>anycompatiblemultirange</type> requires that all arguments declared
+ with that type have the same actual range and/or multirange type, and that
+ that type's subtype agree with the selected common type, so that no casting
+ of the range values is required. As with <type>anyrange</type> and
+ <type>anymultirange</type>, use of <type>anycompatiblerange</type> and
+ <type>anymultirange</type> as a function result type requires that there be
+ an <type>anycompatiblerange</type> or <type>anycompatiblemultirange</type>
+ argument.
</para>
<para>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 12d75b476f..2e7c62f1a0 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -13609,7 +13609,7 @@ SELECT NULLIF(value, '(none)') ...
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14080,12 +14080,14 @@ NULL baz</literallayout>(3 rows)</entry>
<title>Range Functions and Operators</title>
<para>
- See <xref linkend="rangetypes"/> for an overview of range types.
+ See <xref linkend="rangetypes"/> for an overview of range and multirange types.
</para>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14103,134 +14105,215 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
- <entry><literal>f</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">f</literallayout></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
- <entry><literal>[5,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)
+'{[5,10)}'::nummultirange + '{[15,20)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,20)
+{[5,10), [15,20)}</literallayout></entry>
</row>
<row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
- <entry><literal>[10,15)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)
+'{[5,15)}'::int8multirange * '{[10,20)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[10,15)
+{[10,15)}</literallayout></entry>
</row>
<row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
- <entry><literal>[5,10)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)
+'{[5,20)}'::int8multirange - '{[10,15)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,10)
+{[5,10), [15,20)}</literallayout></entry>
</row>
</tbody>
@@ -14248,19 +14331,31 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. There are separate operators for union and difference that take
+ multirange parameters and return a multirange, and they do not fail even if
+ their arguments are disjoint. So if you need a union or difference operation
+ for ranges that may be disjoint, you can avoid errors by first casting your
+ ranges to multiranges.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14312,6 +14407,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14323,6 +14429,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>isempty</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14334,6 +14451,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14345,6 +14473,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14356,6 +14495,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14367,6 +14517,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14378,6 +14539,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>range_merge</function>(<type>anyrange</type>, <type>anyrange</type>)
</literal>
</entry>
@@ -14386,16 +14558,38 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
+ <function>multirange</function>(<type>anyrange</type>)
+ </literal>
+ </entry>
+ <entry><type>anymultirange</type></entry>
+ <entry>a multirange containing just the given range</entry>
+ <entry><literal>multirange('[1,2)'::int4range)</literal></entry>
+ <entry><literal>{[1,2)}</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -14717,6 +14911,44 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>string_agg</primary>
</indexterm>
<function>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index b75fb3a392..c8784bdce3 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -232,10 +239,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -269,6 +296,19 @@ SELECT int8range(1, 14, '(]');
SELECT numrange(NULL, 2.2);
</programlisting>
</para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
+</programlisting>
+ </para>
</sect2>
<sect2 id="rangetypes-discrete">
@@ -342,6 +382,11 @@ SELECT '[1.234, 5.678]'::floatrange;
</para>
<para>
+ When you define your own range you automatically get a corresponding
+ multirange type.
+ </para>
+
+ <para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
ordering that determines which values fall into a given range.
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index b5bc36c2bd..1217a6ddd0 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,12 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index cd56714968..c359f27c74 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -27,6 +27,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
@@ -36,6 +37,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
+ bool tryOriginal);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -809,31 +813,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
char *
makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
- char *arr = (char *) palloc(NAMEDATALEN);
- int namelen = strlen(typeName);
- int i;
-
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
+ char *arr;
- if (i >= NAMEDATALEN - 1)
+ arr = makeUniqueTypeName(typeName, typeNamespace, false);
+ if (arr == NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -904,3 +887,91 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char *buf;
+ char *mrname;
+ char *rangestr;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "_multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ char *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
+
+ buf = psprintf("%s%s%s", prefix, "multi", rangestr);
+ }
+ else
+ buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
+
+ /* clip it at NAMEDATALEN-1 bytes */
+ buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
+
+ mrname = makeUniqueTypeName(buf, typeNamespace, true);
+ if (mrname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mrname;
+}
+
+/*
+ * makeUniqueTypeName
+ * Generate a unique name for a prospective new type
+ *
+ * Given a typeName, return a new palloc'ed name by preprending underscores
+ * until a non-conflicting name results.
+ *
+ * If tryOriginal, first try with zero underscores.
+ */
+static char *
+makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
+{
+ int i;
+ int namelen;
+ char dest[NAMEDATALEN];
+
+ Assert(strlen(typeName) <= NAMEDATALEN - 1);
+
+ if (tryOriginal &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(typeName);
+
+ /*
+ * The idea is to prepend underscores as needed until we make a name that
+ * doesn't collide with anything ...
+ */
+ namelen = strlen(typeName);
+ for (i = 1; i < NAMEDATALEN - 1; i++)
+ {
+ dest[i - 1] = '_';
+ strlcpy(dest + i, typeName, NAMEDATALEN - i);
+ if (namelen + i >= NAMEDATALEN)
+ truncate_identifier(dest, NAMEDATALEN, false);
+
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(dest);
+ }
+
+ return NULL;
+}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 8891b1d564..dfb2edd3d0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -105,9 +105,14 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid,
+ Oid rangeArrayOid, Oid *castFuncOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -738,7 +743,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1279,6 +1285,11 @@ checkEnumOwner(HeapTuple tup)
/*
* DefineRange
* Registers a new range type.
+ *
+ * Perhaps it might be worthwhile to set pg_type.typelem to the base type,
+ * and likewise on multiranges to set it to the range type. But having a
+ * non-zero typelem is treated elsewhere as a synonym for being an array,
+ * and users might have queries with that same assumption.
*/
ObjectAddress
DefineRange(CreateRangeStmt *stmt)
@@ -1287,7 +1298,11 @@ DefineRange(CreateRangeStmt *stmt)
Oid typeNamespace;
Oid typoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1304,6 +1319,8 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
+ Oid castFuncOid;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1452,8 +1469,10 @@ DefineRange(CreateRangeStmt *stmt)
/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for ranges */
alignment = (subtypalign == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
- /* Allocate OID for array type */
+ /* Allocate OID for array type, its multirange, and its multirange array */
rangeArrayOid = AssignTypeArrayOid();
+ multirangeOid = AssignTypeMultirangeOid();
+ multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
address =
@@ -1491,9 +1510,46 @@ DefineRange(CreateRangeStmt *stmt)
Assert(typoid == InvalidOid || typoid == address.objectId);
typoid = address.objectId;
+ /* Create the multirange that goes with it */
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(multirangeOid, /* force assignment of this type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_RANGE, /* type-category (range type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ Assert(multirangeOid == mltrngaddress.objectId);
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
@@ -1534,8 +1590,53 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ multirangeOid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ multirangeOid, typoid, rangeArrayOid,
+ &castFuncOid);
+
+ /* Create cast from the range type to its multirange type */
+ CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1613,6 +1714,147 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ *
+ * Sets castFuncOid to the oid of the new constructor that can be used
+ * to cast from a range to a multirange.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
+ Oid *castFuncOid)
+{
+ ObjectAddress myself,
+ referenced;
+ oidvector *argtypes;
+ Datum allParamTypes;
+ ArrayType *allParameterTypes;
+ Datum paramModes;
+ ArrayType *parameterModes;
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ /* 0-arg constructor - for empty multiranges */
+ argtypes = buildoidvector(NULL, 0);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor0", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /*
+ * Make the constructor internally-dependent on the multirange type so
+ * that they go away silently when the type is dropped. Note that pg_dump
+ * depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+
+ /*
+ * 1-arg constructor - for casts
+ *
+ * In theory we shouldn't need both this and the vararg (n-arg) constructor,
+ * but having a separate 1-arg function lets us define casts against it.
+ */
+ argtypes = buildoidvector(&rangeOid, 1);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor1", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ true, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ *castFuncOid = myself.objectId;
+
+ /* n-arg constructor - vararg */
+ argtypes = buildoidvector(&rangeArrayOid, 1);
+ allParamTypes = ObjectIdGetDatum(rangeArrayOid);
+ allParameterTypes = construct_array(&allParamTypes,
+ 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
+ parameterModes = construct_array(¶mModes, 1, CHAROID,
+ 1, true, 'c');
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor2", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(allParameterTypes), /* allParameterTypes */
+ PointerGetDatum(parameterModes), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ pfree(allParameterTypes);
+ pfree(parameterModes);
+}
/*
* Find suitable I/O functions for a type.
@@ -2045,6 +2287,72 @@ AssignTypeArrayOid(void)
return type_array_oid;
}
+/*
+ * AssignTypeMultirangeOid
+ *
+ * Pre-assign the range type's multirange OID for use in pg_type.oid
+ */
+Oid
+AssignTypeMultirangeOid(void)
+{
+ Oid type_multirange_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
+
+ type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
+ binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_oid;
+}
+
+/*
+ * AssignTypeMultirangeArrayOid
+ *
+ * Pre-assign the range type's multirange array OID for use in pg_type.typarray
+ */
+Oid
+AssignTypeMultirangeArrayOid(void)
+{
+ Oid type_multirange_array_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
+
+ type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
+ binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_array_oid;
+}
+
/*-------------------------------------------------------------------
* DefineCompositeType
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 1c387a952e..e26822361c 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -1693,7 +1693,8 @@ check_sql_fn_retval(List *queryTreeList,
if (fn_typtype == TYPTYPE_BASE ||
fn_typtype == TYPTYPE_DOMAIN ||
fn_typtype == TYPTYPE_ENUM ||
- fn_typtype == TYPTYPE_RANGE)
+ fn_typtype == TYPTYPE_RANGE ||
+ fn_typtype == TYPTYPE_MULTIRANGE)
{
/*
* For scalar-type returns, the target list must have exactly one
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 1b11cf731c..04cbffd7c2 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -189,19 +189,21 @@ coerce_type(ParseState *pstate, Node *node,
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID ||
targetTypeId == ANYCOMPATIBLEARRAYOID ||
- targetTypeId == ANYCOMPATIBLERANGEOID)
+ targetTypeId == ANYCOMPATIBLERANGEOID ||
+ targetTypeId == ANYCOMPATIBLEMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call the pseudotype's input
- * function, which will produce an error. Also, if what we have is a
- * domain over array, enum, or range, we have to relabel it to its
- * base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * the pseudotype's input function, which will produce an error. Also,
+ * if what we have is a domain over array, enum, range, or multirange,
+ * we have to relabel it to its base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1532,8 +1534,8 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* 1) All arguments declared ANYELEMENT must have the same datatype.
* 2) All arguments declared ANYARRAY must have the same datatype,
* which must be a varlena array type.
- * 3) All arguments declared ANYRANGE must have the same datatype,
- * which must be a range type.
+ * 3) All arguments declared ANYRANGE or ANYMULTIRANGE must be a range or
+ * multirange type, all derived from the same base datatype.
* 4) If there are arguments of more than one of these polymorphic types,
* the array element type and/or range subtype must be the same as each
* other and the same as the ANYELEMENT type.
@@ -1548,8 +1550,8 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* to a common supertype (chosen as per select_common_type's rules).
* ANYCOMPATIBLENONARRAY works like ANYCOMPATIBLE but also requires the
* common supertype to not be an array. If there are ANYCOMPATIBLEARRAY
- * or ANYCOMPATIBLERANGE arguments, their element types or subtypes are
- * included while making the choice of common supertype.
+ * or ANYCOMPATIBLERANGE or ANYCOMPATIBLEMULTIRANGE arguments, their element
+ * types or subtypes are included while making the choice of common supertype.
* 8) The resolved type of ANYCOMPATIBLEARRAY arguments will be the array
* type over the common supertype (which might not be the same array type
* as any of the original arrays).
@@ -1557,6 +1559,10 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* (after domain flattening), since we have no preference rule that would
* let us choose one over another. Furthermore, that range's subtype
* must exactly match the common supertype chosen by rule 7.
+ * 10) All ANYCOMPATIBLEMULTIRANGE arguments must be the exact same multirange
+ * type (after domain flattening), since we have no preference rule that would
+ * let us choose one over another. Furthermore, that multirange's range's
+ * subtype must exactly match the common supertype chosen by rule 7.
*
* Domains over arrays match ANYARRAY, and are immediately flattened to their
* base type. (Thus, for example, we will consider it a match if one ANYARRAY
@@ -1565,7 +1571,9 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* for ANYCOMPATIBLEARRAY and ANYCOMPATIBLENONARRAY.
*
* Similarly, domains over ranges match ANYRANGE or ANYCOMPATIBLERANGE,
- * and are immediately flattened to their base type.
+ * and are immediately flattened to their base type, and domains over
+ * multiranges match ANYMULTIRANGE or ANYCOMPATIBLEMULTIRANGE and are immediately
+ * flattened to their base type.
*
* Note that domains aren't currently considered to match ANYENUM,
* even if their base type would match.
@@ -1583,8 +1591,12 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid anycompatible_range_typeid = InvalidOid;
Oid anycompatible_range_typelem = InvalidOid;
+ Oid anycompatible_multirange_typeid = InvalidOid;
+ Oid anycompatible_multirange_typelem = InvalidOid;
+ Oid range_typelem = InvalidOid;
bool have_anynonarray = false;
bool have_anyenum = false;
bool have_anycompatible_nonarray = false;
@@ -1633,6 +1645,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
else if (decl_type == ANYCOMPATIBLEOID ||
decl_type == ANYCOMPATIBLENONARRAYOID)
{
@@ -1677,6 +1698,42 @@ check_generic_type_consistency(const Oid *actual_arg_types,
anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
}
}
+ else if (decl_type == ANYCOMPATIBLEMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(anycompatible_multirange_typeid))
+ {
+ /* All ANYCOMPATIBLEMULTIRANGE arguments must be the same type */
+ if (anycompatible_multirange_typeid != actual_type)
+ return false;
+ }
+ else
+ {
+ anycompatible_multirange_typeid = actual_type;
+ anycompatible_multirange_typelem = get_range_multirange_subtype(actual_type);
+ if (!OidIsValid(anycompatible_multirange_typelem))
+ return false; /* not a multirange type */
+
+ if (OidIsValid(anycompatible_range_typeid))
+ {
+ /* ANYCOMPATIBLEMULTIRANGE and ANYCOMPATIBLERANGE arguments must match */
+ if (anycompatible_range_typeid != anycompatible_multirange_typelem)
+ return false;
+ }
+ else
+ {
+ anycompatible_range_typeid = anycompatible_multirange_typelem;
+ anycompatible_range_typelem = get_range_subtype(anycompatible_range_typeid);
+ if (!OidIsValid(anycompatible_range_typelem))
+ return false; /* not a range type */
+ }
+ /* collect the subtype for common-supertype choice */
+ anycompatible_actual_types[n_anycompatible_args++] =
+ anycompatible_range_typelem;
+ }
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1723,8 +1780,6 @@ check_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- Oid range_typelem;
-
range_typelem = get_range_subtype(range_typeid);
if (!OidIsValid(range_typelem))
return false; /* should be a range, but isn't */
@@ -1743,6 +1798,45 @@ check_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ Oid multirange_typelem;
+
+ multirange_typelem = get_range_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+ }
+
if (have_anynonarray)
{
/* require the element type to not be an array or domain over array */
@@ -1781,8 +1875,10 @@ check_generic_type_consistency(const Oid *actual_arg_types,
}
/*
- * the anycompatible type must exactly match the range element type,
- * if we were able to identify one
+ * The anycompatible type must exactly match the range element type,
+ * if we were able to identify one. This checks compatibility for
+ * anycompatiblemultirange too since that also sets
+ * anycompatible_range_typelem above.
*/
if (OidIsValid(anycompatible_range_typelem) &&
anycompatible_range_typelem != anycompatible_typeid)
@@ -1821,21 +1917,27 @@ check_generic_type_consistency(const Oid *actual_arg_types,
* argument's actual type as the function's return type.
* 2) If return type is ANYARRAY, and any argument is ANYARRAY, use the
* argument's actual type as the function's return type.
- * 3) Similarly, if return type is ANYRANGE, and any argument is ANYRANGE,
- * use the argument's actual type as the function's return type.
- * 4) Otherwise, if return type is ANYELEMENT or ANYARRAY, and there is
+ * 3) Similarly, if return type is ANYRANGE or ANYMULTIRANGE, and any
+ * argument is ANYRANGE or ANYMULTIRANGE, use that argument's
+ * actual type, range type or multirange type as the function's return
+ * type.
+ * 4) Otherwise, if return type is ANYMULTIRANGE, and any argument is
+ * ANYMULTIRANGE, use the argument's actual type as the function's return
+ * type. Or if any argument is ANYRANGE, use its multirange type as the
+ * function's return type.
+ * 5) Otherwise, if return type is ANYELEMENT or ANYARRAY, and there is
* at least one ANYELEMENT, ANYARRAY, or ANYRANGE input, deduce the
* return type from those inputs, or throw error if we can't.
- * 5) Otherwise, if return type is ANYRANGE, throw error. (We have no way to
- * select a specific range type if the arguments don't include ANYRANGE.)
- * 6) ANYENUM is treated the same as ANYELEMENT except that if it is used
+ * 6) Otherwise, if return type is ANYRANGE or ANYMULTIRANGE, throw error.
+ * (We have no way to select a specific range type if the arguments don't
+ * include ANYRANGE.)
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
- * 7) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
+ * 8) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
* we add the extra condition that the ANYELEMENT type must not be an array.
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
* is an extra restriction if not.)
- * 8) ANYCOMPATIBLE, ANYCOMPATIBLEARRAY, ANYCOMPATIBLENONARRAY, and
+ * 9) ANYCOMPATIBLE, ANYCOMPATIBLEARRAY, ANYCOMPATIBLENONARRAY, and
* ANYCOMPATIBLERANGE are handled by resolving the common supertype
* of those arguments (or their element types/subtypes, for array and range
* inputs), and then coercing all those arguments to the common supertype,
@@ -1889,15 +1991,21 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid anycompatible_typeid = InvalidOid;
Oid anycompatible_array_typeid = InvalidOid;
Oid anycompatible_range_typeid = InvalidOid;
Oid anycompatible_range_typelem = InvalidOid;
+ Oid anycompatible_multirange_typeid = InvalidOid;
+ Oid anycompatible_multirange_typelem = InvalidOid;
+ Oid range_typelem;
+ Oid multirange_typelem;
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
bool have_anycompatible_nonarray = (rettype == ANYCOMPATIBLENONARRAYOID);
bool have_anycompatible_array = (rettype == ANYCOMPATIBLEARRAYOID);
bool have_anycompatible_range = (rettype == ANYCOMPATIBLERANGEOID);
+ bool have_anycompatible_multirange = (rettype == ANYCOMPATIBLEMULTIRANGEOID);
int n_poly_args = 0; /* this counts all family-1 arguments */
int n_anycompatible_args = 0; /* this counts only non-unknowns */
Oid anycompatible_actual_types[FUNC_MAX_ARGS];
@@ -1977,6 +2085,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ n_poly_args++;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_poly_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
else if (decl_type == ANYCOMPATIBLEOID ||
decl_type == ANYCOMPATIBLENONARRAYOID)
{
@@ -2045,6 +2173,41 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
}
}
+ else if (decl_type == ANYCOMPATIBLEMULTIRANGEOID)
+ {
+ have_poly_anycompatible = true;
+ have_anycompatible_multirange = true;
+ if (actual_type == UNKNOWNOID)
+ continue;
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(anycompatible_multirange_typeid))
+ {
+ /* All ANYCOMPATIBLEMULTIRANGE arguments must be the same type */
+ if (anycompatible_multirange_typeid != actual_type)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anycompatiblemultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(anycompatible_multirange_typeid),
+ format_type_be(actual_type))));
+ }
+ else
+ {
+ anycompatible_multirange_typeid = actual_type;
+ anycompatible_multirange_typelem = get_range_multirange_subtype(actual_type);
+ anycompatible_range_typelem = get_range_subtype(anycompatible_multirange_typelem);
+ if (!OidIsValid(anycompatible_multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anycompatiblemultirange",
+ format_type_be(actual_type))));
+ /* collect the subtype for common-supertype choice */
+ anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
+ }
+ }
}
/*
@@ -2113,8 +2276,6 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- Oid range_typelem;
-
range_typelem = get_range_subtype(range_typeid);
if (!OidIsValid(range_typelem))
ereport(ERROR,
@@ -2143,6 +2304,61 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(elem_typeid))));
}
}
+ else
+ range_typelem = InvalidOid;
+
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_range_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * if we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
+ }
+ else
+ multirange_typelem = InvalidOid;
if (!OidIsValid(elem_typeid))
{
@@ -2151,6 +2367,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -2250,6 +2467,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
anycompatible_typeid = ANYCOMPATIBLEOID;
anycompatible_array_typeid = ANYCOMPATIBLEARRAYOID;
anycompatible_range_typeid = ANYCOMPATIBLERANGEOID;
+ anycompatible_multirange_typeid = ANYCOMPATIBLEMULTIRANGEOID;
}
else
{
@@ -2281,6 +2499,8 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
declared_arg_types[j] = anycompatible_array_typeid;
else if (decl_type == ANYCOMPATIBLERANGEOID)
declared_arg_types[j] = anycompatible_range_typeid;
+ else if (decl_type == ANYCOMPATIBLEMULTIRANGEOID)
+ declared_arg_types[j] = anycompatible_multirange_typeid;
}
}
@@ -2331,6 +2551,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -2367,6 +2598,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYCOMPATIBLE use the appropriate type */
if (rettype == ANYCOMPATIBLEOID ||
rettype == ANYCOMPATIBLENONARRAYOID)
@@ -2401,6 +2648,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return anycompatible_range_typeid;
}
+ /* if we return ANYCOMPATIBLEMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYCOMPATIBLEMULTIRANGEOID)
+ {
+ /* this error is unreachable if the function signature is valid: */
+ if (!OidIsValid(anycompatible_multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg_internal("could not identify anycompatiblemultirange type")));
+ return anycompatible_multirange_typeid;
+ }
+
/* we don't return a generic type; send back the original return type */
return rettype;
}
@@ -2418,20 +2676,37 @@ check_valid_polymorphic_signature(Oid ret_type,
const Oid *declared_arg_types,
int nargs)
{
- if (ret_type == ANYRANGEOID || ret_type == ANYCOMPATIBLERANGEOID)
+ if (ret_type == ANYRANGEOID || ret_type == ANYMULTIRANGEOID)
{
/*
- * ANYRANGE requires an ANYRANGE input, else we can't tell which of
- * several range types with the same element type to use. Likewise
- * for ANYCOMPATIBLERANGE.
+ * ANYRANGE and ANYMULTIRANGE require an ANYRANGE or ANYMULTIRANGE input,
+ * else we can't tell which of several range types with the same element
+ * type to use.
*/
for (int i = 0; i < nargs; i++)
{
- if (declared_arg_types[i] == ret_type)
+ if (declared_arg_types[i] == ANYRANGEOID ||
+ declared_arg_types[i] == ANYMULTIRANGEOID)
return NULL; /* OK */
}
- return psprintf(_("A result of type %s requires at least one input of type %s."),
- format_type_be(ret_type), format_type_be(ret_type));
+ return psprintf(_("A result of type %s requires at least one input of type anyrange or anymultirange."),
+ format_type_be(ret_type));
+ }
+ else if (ret_type == ANYCOMPATIBLERANGEOID || ret_type == ANYCOMPATIBLEMULTIRANGEOID)
+ {
+ /*
+ * ANYCOMPATIBLERANGE and ANYCOMPATIBLEMULTIRANGE require an ANYCOMPATIBLERANGE or
+ * ANYCOMPATIBLEMULTIRANGE input, else we can't tell which of several range types
+ * with the same element type to use.
+ */
+ for (int i = 0; i < nargs; i++)
+ {
+ if (declared_arg_types[i] == ANYCOMPATIBLERANGEOID ||
+ declared_arg_types[i] == ANYCOMPATIBLEMULTIRANGEOID)
+ return NULL; /* OK */
+ }
+ return psprintf(_("A result of type %s requires at least one input of type anycompatiblerange or anycompatiblemultirange."),
+ format_type_be(ret_type));
}
else if (IsPolymorphicTypeFamily1(ret_type))
{
@@ -2442,7 +2717,7 @@ check_valid_polymorphic_signature(Oid ret_type,
return NULL; /* OK */
}
/* Keep this list in sync with IsPolymorphicTypeFamily1! */
- return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange."),
+ return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange."),
format_type_be(ret_type));
}
else if (IsPolymorphicTypeFamily2(ret_type))
@@ -2594,6 +2869,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID || targettype == ANYCOMPATIBLEMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 5d2aca8cfe..1a08218de9 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..bd3cf3dc93
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,2186 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ FmgrInfo typioproc; /* range type's I/O proc */
+ Oid typioparam; /* range type's I/O parameter */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData *get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks either either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str = NULL;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */ ;
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **)
+ repalloc(ranges, range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(InputFunctionCall(&cache->typioproc,
+ range_str_copy,
+ cache->typioparam,
+ typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->typioproc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(ReceiveFunctionCall(&cache->typioproc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range;
+
+ range = RangeTypePGetDatum(ranges[i]);
+ range = PointerGetDatum(SendFunctionCall(&cache->typioproc, range));
+
+ pq_sendint32(buf, VARSIZE(range) - VARHDRSZ);
+ pq_sendbytes(buf, VARDATA(range), VARSIZE(range) - VARHDRSZ);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ Oid typiofunc;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &typiofunc);
+
+ if (!OidIsValid(typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(typiofunc, &cache->typioproc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ bytelen += MAXALIGN(VARSIZE(ranges[i]));
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ memcpy(ptr, ranges[i], VARSIZE(ranges[i]));
+ ptr += MAXALIGN(VARSIZE(ranges[i]));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of arbitrary ranges into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ *
+ * Returns the number of slots actually used, which may be less than
+ * input_range_count but never more.
+ *
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType *multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor2(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_CARDINALITY_VIOLATION),
+ errmsg("multiranges cannot be constructed from multi-dimensional arrays")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Construct multirange value from a single range.
+ * It'd be nice if we could just use multirange_constructor2
+ * for this case, but we need a non-variadic single-arg function
+ * to let us define a CAST from a range to its multirange.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ RangeType *range;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ range = PG_GETARG_RANGE_P(0);
+
+ /* Make sure the range type matches. */
+ rngtypid = RangeTypeGetOid(range);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 1, &range));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ elog(ERROR, /* can't happen */
+ "niladic multirange constructor must not receive arguments");
+}
+
+
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+multirange_union(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
+}
+
+/* multirange minus */
+Datum
+multirange_minus(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+multirange_intersect(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+ /*-----------------------------------------------
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1,
+ * but one extra won't hurt.
+ *-----------------------------------------------
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType *mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges1[range_count1 - 1],
+ ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype,
+ ranges1[0],
+ ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1, MultirangeType *mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
+ return false;
+
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
+ return false;
+ }
+
+ /* All ranges in mr2 are satisfied */
+ return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 18f2ee8226..248ea7f44a 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -52,6 +52,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
}
Datum
+binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
+binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_array_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
binary_upgrade_set_next_toast_pg_type_oid(PG_FUNCTION_ARGS)
{
Oid typoid = PG_GETARG_OID(0);
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 3d6b2f9093..99a93271fe 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -228,6 +229,43 @@ anycompatiblerange_out(PG_FUNCTION_ARGS)
}
/*
+ * anycompatiblemultirange
+ *
+ * We may as well allow output, since multirange_out will in fact work.
+ */
+PSEUDOTYPE_DUMMY_INPUT_FUNC(anycompatiblemultirange);
+
+Datum
+anycompatiblemultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
+/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
+/*
* void
*
* We support void_in so that PL functions can return VOID without any
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 01ad8bc240..0af3c67296 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -431,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -452,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -475,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -485,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -495,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -505,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -515,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -957,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -969,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -993,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1101,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1110,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1132,9 +1180,81 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
+}
+
+/* range, range -> range, range functions */
+
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
+ {
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
+ }
+
+ return false;
}
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
+}
+
+
/* Btree support */
/* btree comparator */
@@ -1144,12 +1264,6 @@ range_cmp(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
- RangeBound lower1,
- lower2;
- RangeBound upper1,
- upper2;
- bool empty1,
- empty2;
int cmp;
check_stack_depth(); /* recurses when subtype is a range type */
@@ -1160,6 +1274,28 @@ range_cmp(PG_FUNCTION_ARGS)
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+ cmp = range_cmp_internal(typcache, r1, r2);
+
+ PG_FREE_IF_COPY(r1, 0);
+ PG_FREE_IF_COPY(r2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
@@ -1177,10 +1313,7 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
- PG_FREE_IF_COPY(r1, 0);
- PG_FREE_IF_COPY(r2, 1);
-
- PG_RETURN_INT32(cmp);
+ return cmp;
}
/* inequality operators using the range_cmp function */
@@ -1218,13 +1351,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1363,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1404,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1433,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1471,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1770,6 +1917,21 @@ range_get_flags(const RangeType *range)
}
/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
+/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
* This is only needed in GiST operations, so we don't include a provision
@@ -1938,6 +2100,46 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
}
/*
+ * qsort callback for sorting ranges.
+ *
+ * Two empty ranges compare equal; an empty range sorts to the left of any
+ * non-empty range. Two non-empty ranges are sorted by lower bound first
+ * and by upper bound next.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
+/*
* Build an empty range value of the type indicated by the typcache entry.
*/
RangeType *
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index a7d63f107f..b137461de7 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2530,6 +2530,16 @@ type_is_range(Oid typid)
}
/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
+/*
* get_type_category_preferred
*
* Given the type OID, fetch its category and preferred-type status.
@@ -3184,7 +3194,7 @@ get_namespace_name_or_temp(Oid nspid)
return get_namespace_name(nspid);
}
-/* ---------- PG_RANGE CACHE ---------- */
+/* ---------- PG_RANGE CACHES ---------- */
/*
* get_range_subtype
@@ -3237,6 +3247,56 @@ get_range_collation(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngmultitypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_range_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_range_multirange_subtype(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGEMULTIRANGE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 53d9ddf159..7654331a59 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -651,6 +651,18 @@ static const struct cachedesc cacheinfo[] = {
},
64
},
+ {RangeRelationId, /* RANGEMULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngmultitypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+
{RangeRelationId, /* RANGETYPE */
RangeTypidIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 854f133f9b..160846cf10 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -286,6 +286,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -302,6 +303,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -553,8 +557,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -591,7 +595,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -616,7 +620,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -641,7 +645,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -693,6 +697,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -737,6 +748,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -817,6 +835,16 @@ lookup_type_cache(Oid type_id, int flags)
}
/*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
+ /*
* If requested, get information about a domain type
*/
if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
@@ -927,6 +955,22 @@ load_rangetype_info(TypeCacheEntry *typentry)
typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
}
+/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Oid rangetypeOid;
+
+ rangetypeOid = get_range_multirange_subtype(typentry->type_id);
+ if (!OidIsValid(rangetypeOid))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
/*
* load_domaintype_info --- helper routine to set up domain constraint info
@@ -1527,11 +1571,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1574,6 +1618,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 78ed857203..8a5bd9bf19 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -35,6 +35,7 @@ typedef struct polymorphic_actuals
Oid anyelement_type; /* anyelement mapping, if known */
Oid anyarray_type; /* anyarray mapping, if known */
Oid anyrange_type; /* anyrange mapping, if known */
+ Oid anymultirange_type; /* anymultirange mapping, if known */
} polymorphic_actuals;
static void shutdown_MultiFuncCall(Datum arg);
@@ -46,6 +47,7 @@ static TypeFuncClass internal_get_result_type(Oid funcid,
static void resolve_anyelement_from_others(polymorphic_actuals *actuals);
static void resolve_anyarray_from_others(polymorphic_actuals *actuals);
static void resolve_anyrange_from_others(polymorphic_actuals *actuals);
+static void resolve_anymultirange_from_others(polymorphic_actuals *actuals);
static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
oidvector *declared_args,
Node *call_expr);
@@ -503,6 +505,31 @@ resolve_anyelement_from_others(polymorphic_actuals *actuals)
format_type_be(range_base_type))));
actuals->anyelement_type = range_typelem;
}
+ else if (OidIsValid(actuals->anymultirange_type))
+ {
+ /* Use the element type based on the multirange type */
+ Oid multirange_base_type = getBaseType(actuals->anymultirange_type);
+ Oid multirange_typelem =
+ get_range_multirange_subtype(multirange_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_base_type))));
+
+ Oid range_base_type = getBaseType(multirange_typelem);
+ Oid range_typelem = get_range_subtype(range_base_type);
+
+ if (!OidIsValid(range_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s does not contain a range type but type %s",
+ "anymultirange",
+ format_type_be(range_base_type))));
+ actuals->anyelement_type = range_typelem;
+ }
else
elog(ERROR, "could not determine polymorphic type");
}
@@ -540,10 +567,54 @@ static void
resolve_anyrange_from_others(polymorphic_actuals *actuals)
{
/*
- * We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types with the same subtype.
+ * We can't deduce a range type from other polymorphic array or base types,
+ * because there may be multiple range types with the same subtype, but we
+ * can deduce it from a polymorphic multirange type.
*/
- elog(ERROR, "could not determine polymorphic type");
+ if (OidIsValid(actuals->anymultirange_type))
+ {
+ /* Use the element type based on the multirange type */
+ Oid multirange_base_type = getBaseType(actuals->anymultirange_type);
+ Oid multirange_typelem =
+ get_range_multirange_subtype(multirange_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_base_type))));
+ actuals->anyrange_type = multirange_typelem;
+ }
+ else
+ elog(ERROR, "could not determine polymorphic type");
+}
+
+/*
+ * Resolve actual type of ANYMULTIRANGE from other polymorphic inputs
+ */
+static void
+resolve_anymultirange_from_others(polymorphic_actuals *actuals)
+{
+ /*
+ * We can't deduce a multirange type from polymorphic array or base types,
+ * because there may be multiple range types with the same subtype, but we
+ * can deduce it from a polymorphic range type.
+ */
+ if (OidIsValid(actuals->anyrange_type))
+ {
+ Oid range_base_type = getBaseType(actuals->anyrange_type);
+ Oid multirange_typeid = get_range_multirange(range_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(actuals->anyrange_type))));
+ actuals->anymultirange_type = multirange_typeid;
+ }
+ else
+ elog(ERROR, "could not determine polymorphic type");
}
/*
@@ -566,9 +637,11 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anycompatible_result = false;
bool have_anycompatible_array_result = false;
bool have_anycompatible_range_result = false;
+ bool have_anycompatible_multirange_result = false;
polymorphic_actuals poly_actuals;
polymorphic_actuals anyc_actuals;
Oid anycollation = InvalidOid;
@@ -594,6 +667,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
have_polymorphic_result = true;
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_polymorphic_result = true;
+ have_anymultirange_result = true;
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
have_polymorphic_result = true;
@@ -607,6 +684,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
have_polymorphic_result = true;
have_anycompatible_range_result = true;
break;
+ case ANYCOMPATIBLEMULTIRANGEOID:
+ have_polymorphic_result = true;
+ have_anycompatible_multirange_result = true;
+ break;
default:
break;
}
@@ -660,6 +741,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
}
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ {
+ poly_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ return false;
+ }
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
if (!OidIsValid(anyc_actuals.anyelement_type))
@@ -688,6 +778,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
}
break;
+ case ANYCOMPATIBLEMULTIRANGEOID:
+ if (!OidIsValid(anyc_actuals.anymultirange_type))
+ {
+ anyc_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(anyc_actuals.anymultirange_type))
+ return false;
+ }
+ break;
default:
break;
}
@@ -703,6 +802,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&poly_actuals);
+
if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
resolve_anyelement_from_others(&anyc_actuals);
@@ -712,6 +814,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
resolve_anyrange_from_others(&anyc_actuals);
+ if (have_anycompatible_multirange_result && !OidIsValid(anyc_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&anyc_actuals);
+
/*
* Identify the collation to use for polymorphic OUT parameters. (It'll
* necessarily be the same for both anyelement and anyarray, likewise for
@@ -780,6 +885,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ poly_actuals.anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
TupleDescInitEntry(tupdesc, i + 1,
@@ -805,6 +918,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYCOMPATIBLEMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anyc_actuals.anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
default:
break;
}
@@ -834,9 +955,11 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anycompatible_result = false;
bool have_anycompatible_array_result = false;
bool have_anycompatible_range_result = false;
+ bool have_anycompatible_multirange_result = false;
polymorphic_actuals poly_actuals;
polymorphic_actuals anyc_actuals;
int inargno;
@@ -912,6 +1035,24 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = poly_actuals.anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
+ have_anymultirange_result = true;
+ }
+ else
+ {
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ {
+ poly_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ return false;
+ }
+ argtypes[i] = poly_actuals.anymultirange_type;
+ }
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
@@ -967,6 +1108,24 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyc_actuals.anyrange_type;
}
break;
+ case ANYCOMPATIBLEMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
+ have_anycompatible_multirange_result = true;
+ }
+ else
+ {
+ if (!OidIsValid(anyc_actuals.anymultirange_type))
+ {
+ anyc_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(anyc_actuals.anymultirange_type))
+ return false;
+ }
+ argtypes[i] = anyc_actuals.anymultirange_type;
+ }
+ break;
default:
break;
}
@@ -988,6 +1147,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&poly_actuals);
+
if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
resolve_anyelement_from_others(&anyc_actuals);
@@ -997,6 +1159,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
resolve_anyrange_from_others(&anyc_actuals);
+ if (have_anycompatible_multirange_result && !OidIsValid(anyc_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&anyc_actuals);
+
/* And finally replace the output column types as needed */
for (i = 0; i < numargs; i++)
{
@@ -1013,6 +1178,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = poly_actuals.anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = poly_actuals.anymultirange_type;
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
argtypes[i] = anyc_actuals.anyelement_type;
@@ -1023,6 +1191,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYCOMPATIBLERANGEOID:
argtypes[i] = anyc_actuals.anyrange_type;
break;
+ case ANYCOMPATIBLEMULTIRANGEOID:
+ argtypes[i] = anyc_actuals.anymultirange_type;
+ break;
default:
break;
}
@@ -1052,6 +1223,7 @@ get_type_func_class(Oid typid, Oid *base_typeid)
case TYPTYPE_BASE:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
+ case TYPTYPE_MULTIRANGE:
return TYPEFUNC_SCALAR;
case TYPTYPE_DOMAIN:
*base_typeid = typid = getBaseType(typid);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index c579227b19..5d09aa3d1e 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -276,7 +276,8 @@ static void dumpSearchPath(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type);
+ bool force_array_type,
+ bool include_multirange_type);
static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
static void binary_upgrade_set_pg_class_oids(Archive *fout,
@@ -1648,7 +1649,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4432,16 +4433,49 @@ append_depends_on_extension(Archive *fout,
}
}
+static Oid
+get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+{
+ /*
+ * If the old version didn't assign an array type, but the new version
+ * does, we must select an unused type OID to assign. This currently only
+ * happens for domains, when upgrading pre-v11 to v11 and up.
+ *
+ * Note: local state here is kind of ugly, but we must have some, since we
+ * mustn't choose the same unused OID more than once.
+ */
+ static Oid next_possible_free_oid = FirstNormalObjectId;
+ PGresult *res;
+ bool is_dup;
+
+ do
+ {
+ ++next_possible_free_oid;
+ printfPQExpBuffer(upgrade_query,
+ "SELECT EXISTS(SELECT 1 "
+ "FROM pg_catalog.pg_type "
+ "WHERE oid = '%u'::pg_catalog.oid);",
+ next_possible_free_oid);
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+ is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
+ PQclear(res);
+ } while (is_dup);
+
+ return next_possible_free_oid;
+}
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type)
+ bool force_array_type,
+ bool include_multirange_type)
{
PQExpBuffer upgrade_query = createPQExpBuffer();
PGresult *res;
Oid pg_type_array_oid;
+ Oid pg_type_multirange_oid;
+ Oid pg_type_multirange_array_oid;
appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
@@ -4462,33 +4496,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQclear(res);
if (!OidIsValid(pg_type_array_oid) && force_array_type)
- {
- /*
- * If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
- *
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
- */
- static Oid next_possible_free_oid = FirstNormalObjectId;
- bool is_dup;
-
- do
- {
- ++next_possible_free_oid;
- printfPQExpBuffer(upgrade_query,
- "SELECT EXISTS(SELECT 1 "
- "FROM pg_catalog.pg_type "
- "WHERE oid = '%u'::pg_catalog.oid);",
- next_possible_free_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
- is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
- PQclear(res);
- } while (is_dup);
-
- pg_type_array_oid = next_possible_free_oid;
- }
+ pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
if (OidIsValid(pg_type_array_oid))
{
@@ -4499,6 +4507,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
pg_type_array_oid);
}
+ /*
+ * Pre-set the multirange type oid and its own array type oid.
+ */
+ if (include_multirange_type)
+ {
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBuffer(upgrade_query,
+ "SELECT t.oid, t.typarray "
+ "FROM pg_catalog.pg_type t "
+ "JOIN pg_catalog.pg_range r "
+ "ON t.oid = r.rngmultitypid "
+ "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
+ pg_type_oid);
+
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+
+ pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+ pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
+
+ PQclear(res);
+ }
+ else
+ {
+ pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ }
+
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_oid);
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_array_oid);
+ }
+
destroyPQExpBuffer(upgrade_query);
}
@@ -4532,7 +4580,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
- pg_type_oid, false);
+ pg_type_oid, false, false);
if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
{
@@ -5081,6 +5129,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
@@ -8219,9 +8272,12 @@ getProcLangs(Archive *fout, int *numProcLangs)
/*
* getCasts
- * get basic information about every cast in the system
+ * get basic information about most casts in the system
*
* numCasts is set to the number of casts read in
+ *
+ * Skip casts from a range to its multirange, since we'll create those
+ * automatically.
*/
CastInfo *
getCasts(Archive *fout, int *numCasts)
@@ -8239,7 +8295,20 @@ getCasts(Archive *fout, int *numCasts)
int i_castcontext;
int i_castmethod;
- if (fout->remoteVersion >= 80400)
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBufferStr(query, "SELECT tableoid, oid, "
+ "castsource, casttarget, castfunc, castcontext, "
+ "castmethod "
+ "FROM pg_cast c "
+ "WHERE NOT EXISTS ( "
+ "SELECT 1 FROM pg_range r "
+ "WHERE c.castsource = r.rngtypid "
+ "AND c.casttarget = r.rngmultitypid "
+ ") "
+ "ORDER BY 3,4");
+ }
+ else if (fout->remoteVersion >= 80400)
{
appendPQExpBufferStr(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
@@ -10427,7 +10496,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
qualtypname);
@@ -10553,7 +10622,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, true);
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
qualtypname);
@@ -10659,7 +10728,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
qualtypname);
@@ -10864,7 +10933,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
@@ -11051,7 +11120,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- true); /* force array type */
+ true, /* force array type */
+ false); /* force multirange type */
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -11239,7 +11309,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
{
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
}
@@ -11513,7 +11583,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
stinfo->baseType->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
fmtQualifiedDumpable(stinfo));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 61c909e06d..36178db881 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -176,6 +176,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 70157cf90a..c262265b95 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -139,8 +139,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 12d94fe1b3..01d95117cd 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,6 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 8be303870f..566f9364c5 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -327,6 +327,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_rngmultitypid_index, 8001, on pg_range using btree(rngmultitypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index ffabe275c0..03bab74253 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index b5dfaad9ec..604286e2c2 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1378,6 +1378,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 37b580883f..9842e80acf 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -285,6 +285,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
{ amprocfamily => 'btree/xid8_ops', amproclefttype => 'xid8',
@@ -453,6 +455,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
@@ -1274,12 +1281,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index 5a58f50fbb..d6ca624add 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -530,4 +530,17 @@
{ castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
castcontext => 'e', castmethod => 'f' },
+# range to multirange
+{ castsource => 'int4range', casttarget => 'int4multirange', castfunc => 'int4multirange(int4range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'int8range', casttarget => 'int8multirange', castfunc => 'int8multirange(int8range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'numrange', casttarget => 'nummultirange', castfunc => 'nummultirange(numrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'daterange', casttarget => 'datemultirange', castfunc => 'datemultirange(daterange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tsrange', casttarget => 'tsmultirange', castfunc => 'tsmultirange(tsrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tstzrange', casttarget => 'tstzmultirange', castfunc => 'tstzmultirange(tstzrange)',
+ castcontext => 'e', castmethod => 'f' },
]
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index f2342bb328..9b0609defa 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -230,6 +230,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 00ada7e48f..071352539f 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3327,5 +3327,174 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'matchingsel', oprjoin => 'matchingjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 4004138d77..dfc13fd6e5 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -230,5 +230,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 4bce3ad8de..5eb505fdda 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7208,6 +7208,14 @@
proname => 'anycompatiblerange_out', provolatile => 's',
prorettype => 'cstring', proargtypes => 'anycompatiblerange',
prosrc => 'anycompatiblerange_out' },
+{ oid => '8154', descr => 'I/O',
+ proname => 'anycompatiblemultirange_in', provolatile => 's',
+ prorettype => 'anycompatiblemultirange', proargtypes => 'cstring oid int4',
+ prosrc => 'anycompatiblemultirange_in' },
+{ oid => '8155', descr => 'I/O',
+ proname => 'anycompatiblemultirange_out', provolatile => 's',
+ prorettype => 'cstring', proargtypes => 'anycompatiblemultirange',
+ prosrc => 'anycompatiblemultirange_out' },
# tablesample method handlers
{ oid => '3313', descr => 'BERNOULLI tablesample method handler',
@@ -9797,6 +9805,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9846,6 +9858,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9914,6 +9933,258 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8114',
+ proname => 'multirange_union', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union' },
+{ oid => '8120',
+ proname => 'multirange_minus', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus' },
+{ oid => '8126',
+ proname => 'multirange_intersect', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8146', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 't',
+ prorettype => 'int4multirange', proargtypes => 'int4range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8147', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 't',
+ prorettype => 'nummultirange', proargtypes => 'numrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8148', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 't',
+ prorettype => 'tsmultirange', proargtypes => 'tsrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8149', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 't',
+ prorettype => 'tstzmultirange', proargtypes => 'tstzrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8150', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 't',
+ prorettype => 'datemultirange', proargtypes => 'daterange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8151', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 't',
+ prorettype => 'int8multirange', proargtypes => 'int8range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8152', descr => 'anymultirange cast',
+ proname => 'multirange', proisstrict => 't',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
@@ -10296,6 +10567,14 @@
proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
+{ oid => '8144', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
+{ oid => '8145', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
{ oid => '3585', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index 479754c245..10060255c9 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ rngmultitypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', rngmultitypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', rngmultitypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', rngmultitypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ rngmultitypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ rngmultitypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index 98172bb1b6..60a8fde518 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -34,6 +34,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* OID of range's element type (subtype) */
Oid rngsubtype BKI_LOOKUP(pg_type);
+ /* OID of the range's multirange type */
+ Oid rngmultitypid BKI_LOOKUP(pg_type);
+
/* collation for this range type, or 0 */
Oid rngcollation BKI_DEFAULT(0);
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 78f44af5e7..741090cfe5 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -500,6 +500,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -629,5 +663,16 @@
typtype => 'p', typcategory => 'P', typinput => 'anycompatiblerange_in',
typoutput => 'anycompatiblerange_out', typreceive => '-', typsend => '-',
typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '8153',
+ descr => 'pseudo-type representing a multirange over a polymorphic common type',
+ typname => 'anycompatiblemultirange', typlen => '-1', typbyval => 'f',
+ typtype => 'p', typcategory => 'P', typinput => 'anycompatiblemultirange_in',
+ typoutput => 'anycompatiblemultirange_out', typreceive => '-', typsend => '-',
+ typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 7b37562648..63e7c940a2 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -263,6 +263,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -304,13 +305,15 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#define IsPolymorphicTypeFamily2(typid) \
((typid) == ANYCOMPATIBLEOID || \
(typid) == ANYCOMPATIBLEARRAYOID || \
(typid) == ANYCOMPATIBLENONARRAYOID || \
- (typid) == ANYCOMPATIBLERANGEOID)
+ (typid) == ANYCOMPATIBLERANGEOID || \
+ (typid) == ANYCOMPATIBLEMULTIRANGEOID)
#endif /* EXPOSE_TO_CLIENT_CODE */
@@ -369,4 +372,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 23130895af..989914dfe1 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
+extern Oid AssignTypeMultirangeOid(void);
+extern Oid AssignTypeMultirangeArrayOid(void);
extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index c9c68e2f4f..d940d367f1 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -156,6 +156,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -182,6 +183,8 @@ extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
extern Oid get_range_collation(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_range_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
extern bool get_index_isreplident(Oid index_oid);
extern bool get_index_isvalid(Oid index_oid);
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..469444776c
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern MultirangeType *multirange_minus_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType *multirange_intersect_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType *range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType *make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType *make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 0cbbf09033..204aee4054 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
@@ -92,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -115,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -125,6 +141,7 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
@@ -132,8 +149,12 @@ extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f27b73d76d..e8f393520a 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -79,6 +79,7 @@ enum SysCacheIdentifier
PUBLICATIONOID,
PUBLICATIONREL,
PUBLICATIONRELMAP,
+ RANGEMULTIRANGE,
RANGETYPE,
RELNAMENSP,
RELOID,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index cdd20e56d7..33f332a141 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -101,6 +101,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
/*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype; /* multirange's range underlying type */
+
+ /*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
*/
@@ -143,6 +148,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 828ff5a288..1e3ff02289 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -513,6 +513,8 @@ do_compile(FunctionCallInfo fcinfo,
else if (rettypeid == ANYRANGEOID ||
rettypeid == ANYCOMPATIBLERANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY or ANYCOMPATIBLE */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2089,6 +2091,7 @@ build_datatype(HeapTuple typeTup, int32 typmod,
case TYPTYPE_BASE:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
+ case TYPTYPE_MULTIRANGE:
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_COMPOSITE:
@@ -2507,6 +2510,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYCOMPATIBLERANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..8232795148 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -140,6 +140,7 @@ owner of sequence deptest_a_seq
owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
+owner of type deptest_multirange
owner of type deptest_range
owner of table deptest2
owner of sequence ss1
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..f8ca946d6a
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,2436 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+ int4multirange
+----------------
+ {}
+(1 row)
+
+select int4range(1, 3)::int4multirange;
+ int4range
+-----------
+ {[1,3)}
+(1 row)
+
+select int4range(1, null)::int4multirange;
+ int4range
+-----------
+ {[1,)}
+(1 row)
+
+select int4range(null, null)::int4multirange;
+ int4range
+-----------
+ {(,)}
+(1 row)
+
+select 'empty'::textrange::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textrange('a', 'c')::textmultirange;
+ textrange
+-----------
+ {[a,c)}
+(1 row)
+
+select textrange('a', null)::textmultirange;
+ textrange
+-----------
+ {[a,)}
+(1 row)
+
+select textrange(null, null)::textmultirange;
+ textrange
+-----------
+ {(,)}
+(1 row)
+
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT nummultirange() + nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT nummultirange() - nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() * nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+create function anycompatiblearray_anycompatiblemultirange_func(a anycompatiblearray, mr anycompatiblemultirange)
+ returns anycompatible as 'select $1[1] + lower($2);' language sql;
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1,2], multirange(int4range(10,20)));
+ anycompatiblearray_anycompatiblemultirange_func
+-------------------------------------------------
+ 11
+(1 row)
+
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1,2], multirange(numrange(10,20)));
+ anycompatiblearray_anycompatiblemultirange_func
+-------------------------------------------------
+ 11
+(1 row)
+
+-- should fail
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1.1,2], multirange(int4range(10,20)));
+ERROR: function anycompatiblearray_anycompatiblemultirange_func(numeric[], int4multirange) does not exist
+LINE 1: select anycompatiblearray_anycompatiblemultirange_func(ARRAY...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anycompatiblearray_anycompatiblemultirange_func(anycompatiblearray, anycompatiblemultirange);
+create function anycompatiblerange_anycompatiblemultirange_func(r anycompatiblerange, mr anycompatiblemultirange)
+ returns anycompatible as 'select lower($1) + lower($2);' language sql;
+select anycompatiblerange_anycompatiblemultirange_func(int4range(1,2), multirange(int4range(10,20)));
+ anycompatiblerange_anycompatiblemultirange_func
+-------------------------------------------------
+ 11
+(1 row)
+
+-- should fail
+select anycompatiblerange_anycompatiblemultirange_func(numrange(1,2), multirange(int4range(10,20)));
+ERROR: function anycompatiblerange_anycompatiblemultirange_func(numrange, int4multirange) does not exist
+LINE 1: select anycompatiblerange_anycompatiblemultirange_func(numra...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anycompatiblerange_anycompatiblemultirange_func(anycompatiblerange, anycompatiblemultirange);
+-- should fail
+create function bogus_func(anycompatible)
+ returns anycompatiblerange as 'select int4range(1,10)' language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+ mr_polymorphic
+----------------
+ {[1,4)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 8d350ab61b..044ce8835e 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -208,6 +215,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -228,6 +237,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -340,7 +351,8 @@ WHERE p1.prorettype IN
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+----------------
@@ -355,20 +367,25 @@ ORDER BY 2;
3532 | enum_recv
(9 rows)
--- anyrange is tighter than the rest, can only resolve from anyrange input
+-- anyrange and anymultirange are tighter than the rest, can only resolve
+-- from each other
SELECT p1.oid, p1.proname
FROM pg_proc as p1
-WHERE p1.prorettype = 'anyrange'::regtype
+WHERE p1.prorettype IN ('anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
- 'anyrange'::regtype = ANY (p1.proargtypes)
+ ('anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
+ 8002 | anymultirange_in
3832 | anyrange_in
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(4 rows)
+(7 rows)
-- similarly for the anycompatible family
SELECT p1.oid, p1.proname
@@ -2193,13 +2210,14 @@ ORDER BY 1, 2, 3;
| float_ops | float4_ops | real
| float_ops | float8_ops | double precision
| jsonb_ops | jsonb_ops | jsonb
+ | multirange_ops | multirange_ops | anymultirange
| numeric_ops | numeric_ops | numeric
| range_ops | range_ops | anyrange
| record_image_ops | record_image_ops | record
| record_ops | record_ops | record
| tsquery_ops | tsquery_ops | tsquery
| tsvector_ops | tsvector_ops | tsvector
-(14 rows)
+(15 rows)
-- **************** pg_index ****************
-- Look for illegal values in pg_index fields.
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index d0a6b630b8..d55006d8c9 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -1811,7 +1811,7 @@ begin
return array[x + 1, x + 2];
end$$ language plpgsql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function f1(x anyrange) returns anyarray as $$
begin
return array[lower(x), upper(x)];
@@ -1856,7 +1856,7 @@ begin
return array[x + 1, x + 2];
end$$ language plpgsql;
ERROR: cannot determine result data type
-DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
create function f1(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
begin
return x;
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index 1ff40764d9..248c869931 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -61,7 +61,7 @@ create function polyf(x anyelement) returns anyrange as $$
select array[x + 1, x + 2]
$$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function polyf(x anyrange) returns anyarray as $$
select array[lower(x), upper(x)]
$$ language sql;
@@ -97,12 +97,27 @@ LINE 1: select polyf(int4range(42, 49), 11, 4.5) as fail;
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
drop function polyf(x anycompatiblerange, y anycompatible, z anycompatible);
+create function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
+ select array[lower(x), upper(x), y, z]
+$$ language sql;
+select polyf(multirange(int4range(42, 49)), 11, 2::smallint) as int, polyf(multirange(float8range(4.5, 7.8)), 7.8, 11::real) as num;
+ int | num
+--------------+------------------
+ {42,49,11,2} | {4.5,7.8,7.8,11}
+(1 row)
+
+select polyf(multirange(int4range(42, 49)), 11, 4.5) as fail; -- range type doesn't fit
+ERROR: function polyf(int4multirange, integer, numeric) does not exist
+LINE 1: select polyf(multirange(int4range(42, 49)), 11, 4.5) as fail...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible);
-- fail, can't infer type:
create function polyf(x anycompatible) returns anycompatiblerange as $$
select array[x + 1, x + 2]
$$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
create function polyf(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
select x
$$ language sql;
@@ -113,6 +128,22 @@ select polyf(int4range(42, 49), array[11]) as int, polyf(float8range(4.5, 7.8),
(1 row)
drop function polyf(x anycompatiblerange, y anycompatiblearray);
+-- fail, can't infer type:
+create function polyf(x anycompatible) returns anycompatiblemultirange as $$
+ select array[x + 1, x + 2]
+$$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anycompatiblemultirange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
+create function polyf(x anycompatiblemultirange, y anycompatiblearray) returns anycompatiblemultirange as $$
+ select x
+$$ language sql;
+select polyf(multirange(int4range(42, 49)), array[11]) as int, polyf(multirange(float8range(4.5, 7.8)), array[7]) as num;
+ int | num
+-----------+-------------
+ {[42,49)} | {[4.5,7.8)}
+(1 row)
+
+drop function polyf(x anycompatiblemultirange, y anycompatiblearray);
create function polyf(a anyelement, b anyarray,
c anycompatible, d anycompatible,
OUT x anyarray, OUT y anycompatiblearray)
@@ -231,7 +262,7 @@ CREATE AGGREGATE myaggp01a(*) (SFUNC = stfnp, STYPE = int4[],
CREATE AGGREGATE myaggp02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- N P
-- should CREATE
CREATE AGGREGATE myaggp03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -243,11 +274,11 @@ CREATE AGGREGATE myaggp03b(*) (SFUNC = stfp, STYPE = int4[],
CREATE AGGREGATE myaggp04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp04b(*) (SFUNC = stfp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- Case2 (R = P) && ((B = P) || (B = N))
-- -------------------------------------
-- S tf1 B tf2
@@ -302,13 +333,13 @@ ERROR: function tfp(integer[], anyelement) does not exist
CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N N P
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N P N
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -324,21 +355,21 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P N P
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P P N
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p,
@@ -368,11 +399,11 @@ CREATE AGGREGATE myaggn01b(*) (SFUNC = stfnp, STYPE = int4[],
CREATE AGGREGATE myaggn02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn02b(*) (SFUNC = stfnp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- N P
-- should CREATE
CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -382,7 +413,7 @@ CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
CREATE AGGREGATE myaggn04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- Case4 (R = N) && ((B = P) || (B = N))
-- -------------------------------------
-- S tf1 B tf2
@@ -436,21 +467,21 @@ ERROR: function tfp(integer[], anyelement) does not exist
CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N N P
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N P N
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -472,13 +503,13 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P N P
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P P N
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p,
@@ -1853,7 +1884,59 @@ returns anycompatiblerange as $$
select $1
$$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
+create function anyctest(anycompatible, anycompatiblemultirange)
+returns anycompatiblemultirange as $$
+ select $2
+$$ language sql;
+select x, pg_typeof(x) from anyctest(11, multirange(int4range(4,7))) x;
+ x | pg_typeof
+---------+----------------
+ {[4,7)} | int4multirange
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, multirange(numrange(4,7))) x;
+ x | pg_typeof
+---------+---------------
+ {[4,7)} | nummultirange
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, 12) x; -- fail
+ERROR: function anyctest(integer, integer) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11, 12) x;
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select x, pg_typeof(x) from anyctest(11.2, multirange(int4range(4,7))) x; -- fail
+ERROR: function anyctest(numeric, int4multirange) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11.2, multirange(int4ra...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select x, pg_typeof(x) from anyctest(11.2, '{[4,7)}') x; -- fail
+ERROR: could not identify anycompatiblemultirange type
+drop function anyctest(anycompatible, anycompatiblemultirange);
+create function anyctest(anycompatiblemultirange, anycompatiblemultirange)
+returns anycompatible as $$
+ select lower($1) + upper($2)
+$$ language sql;
+select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(int4range(4,7))) x;
+ x | pg_typeof
+----+-----------
+ 18 | integer
+(1 row)
+
+select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(numrange(4,7))) x; -- fail
+ERROR: function anyctest(int4multirange, nummultirange) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(multirange(int4range(11...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyctest(anycompatiblemultirange, anycompatiblemultirange);
+-- fail, can't infer result type:
+create function anyctest(anycompatible)
+returns anycompatiblemultirange as $$
+ select $1
+$$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anycompatiblemultirange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
create function anyctest(anycompatiblenonarray, anycompatiblenonarray)
returns anycompatiblearray as $$
select array[$1, $2]
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 7eced28452..71e2769f44 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -1556,7 +1556,7 @@ DROP FUNCTION dup(anyelement);
CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE FUNCTION dup (f1 anycompatible, f2 anycompatiblearray, f3 out anycompatible, f4 out anycompatiblearray)
AS 'select $1, $2' LANGUAGE sql;
SELECT dup(22, array[44]);
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index c421f5394f..e9f9e1a11b 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1371,12 +1389,12 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function range_add_bounds(anyrange)
returns anyelement as 'select lower($1) + upper($1)' language sql;
select range_add_bounds(int4range(1, 17));
@@ -1430,7 +1448,7 @@ drop function anycompatiblearray_anycompatiblerange_func(anycompatiblearray, any
create function bogus_func(anycompatible)
returns anycompatiblerange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
--
-- Arrays of ranges
--
@@ -1508,6 +1526,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1525,6 +1544,16 @@ select * from outparam2_succeed(int4range(1,11));
{1,11} | {11,1}
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1547,14 +1576,14 @@ select * from table_succeed(int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 192445878d..d9ce961be2 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
numrange_test|t
onek|t
onek2|t
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 274130e706..8df1ae47e4 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -195,18 +195,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -240,17 +241,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -318,18 +320,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -363,17 +366,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -631,3 +635,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
+ rngtypid | rngsubtype | rngmultitypid
+----------+------------+---------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 95f1925072..9575275179 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes xid
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes xid multirangetypes
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 8ba4136220..4a2a841c22 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -20,6 +20,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..842a7d091a
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,656 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+select int4range(1, 3)::int4multirange;
+select int4range(1, null)::int4multirange;
+select int4range(null, null)::int4multirange;
+select 'empty'::textrange::textmultirange;
+select textrange('a', 'c')::textmultirange;
+select textrange('a', null)::textmultirange;
+select textrange(null, null)::textmultirange;
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT nummultirange() + nummultirange();
+SELECT nummultirange() + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT nummultirange() - nummultirange();
+SELECT nummultirange() - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+SELECT nummultirange() * nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+create function anycompatiblearray_anycompatiblemultirange_func(a anycompatiblearray, mr anycompatiblemultirange)
+ returns anycompatible as 'select $1[1] + lower($2);' language sql;
+
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1,2], multirange(int4range(10,20)));
+
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1,2], multirange(numrange(10,20)));
+
+-- should fail
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1.1,2], multirange(int4range(10,20)));
+
+drop function anycompatiblearray_anycompatiblemultirange_func(anycompatiblearray, anycompatiblemultirange);
+
+create function anycompatiblerange_anycompatiblemultirange_func(r anycompatiblerange, mr anycompatiblemultirange)
+ returns anycompatible as 'select lower($1) + lower($2);' language sql;
+
+select anycompatiblerange_anycompatiblemultirange_func(int4range(1,2), multirange(int4range(10,20)));
+
+-- should fail
+select anycompatiblerange_anycompatiblemultirange_func(numrange(1,2), multirange(int4range(10,20)));
+
+drop function anycompatiblerange_anycompatiblemultirange_func(anycompatiblerange, anycompatiblemultirange);
+
+-- should fail
+create function bogus_func(anycompatible)
+ returns anycompatiblerange as 'select int4range(1,10)' language sql;
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 9a1ea3d999..69c4859102 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -279,16 +290,19 @@ WHERE p1.prorettype IN
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
--- anyrange is tighter than the rest, can only resolve from anyrange input
+-- anyrange and anymultirange are tighter than the rest, can only resolve
+-- from each other
SELECT p1.oid, p1.proname
FROM pg_proc as p1
-WHERE p1.prorettype = 'anyrange'::regtype
+WHERE p1.prorettype IN ('anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
- 'anyrange'::regtype = ANY (p1.proargtypes)
+ ('anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- similarly for the anycompatible family
diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql
index e5222f1f81..ac52c387bb 100644
--- a/src/test/regress/sql/polymorphism.sql
+++ b/src/test/regress/sql/polymorphism.sql
@@ -71,6 +71,16 @@ select polyf(int4range(42, 49), 11, 4.5) as fail; -- range type doesn't fit
drop function polyf(x anycompatiblerange, y anycompatible, z anycompatible);
+create function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
+ select array[lower(x), upper(x), y, z]
+$$ language sql;
+
+select polyf(multirange(int4range(42, 49)), 11, 2::smallint) as int, polyf(multirange(float8range(4.5, 7.8)), 7.8, 11::real) as num;
+
+select polyf(multirange(int4range(42, 49)), 11, 4.5) as fail; -- range type doesn't fit
+
+drop function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible);
+
-- fail, can't infer type:
create function polyf(x anycompatible) returns anycompatiblerange as $$
select array[x + 1, x + 2]
@@ -84,6 +94,19 @@ select polyf(int4range(42, 49), array[11]) as int, polyf(float8range(4.5, 7.8),
drop function polyf(x anycompatiblerange, y anycompatiblearray);
+-- fail, can't infer type:
+create function polyf(x anycompatible) returns anycompatiblemultirange as $$
+ select array[x + 1, x + 2]
+$$ language sql;
+
+create function polyf(x anycompatiblemultirange, y anycompatiblearray) returns anycompatiblemultirange as $$
+ select x
+$$ language sql;
+
+select polyf(multirange(int4range(42, 49)), array[11]) as int, polyf(multirange(float8range(4.5, 7.8)), array[7]) as num;
+
+drop function polyf(x anycompatiblemultirange, y anycompatiblearray);
+
create function polyf(a anyelement, b anyarray,
c anycompatible, d anycompatible,
OUT x anyarray, OUT y anycompatiblearray)
@@ -994,6 +1017,35 @@ returns anycompatiblerange as $$
select $1
$$ language sql;
+create function anyctest(anycompatible, anycompatiblemultirange)
+returns anycompatiblemultirange as $$
+ select $2
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(11, multirange(int4range(4,7))) x;
+select x, pg_typeof(x) from anyctest(11, multirange(numrange(4,7))) x;
+select x, pg_typeof(x) from anyctest(11, 12) x; -- fail
+select x, pg_typeof(x) from anyctest(11.2, multirange(int4range(4,7))) x; -- fail
+select x, pg_typeof(x) from anyctest(11.2, '{[4,7)}') x; -- fail
+
+drop function anyctest(anycompatible, anycompatiblemultirange);
+
+create function anyctest(anycompatiblemultirange, anycompatiblemultirange)
+returns anycompatible as $$
+ select lower($1) + upper($2)
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(int4range(4,7))) x;
+select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(numrange(4,7))) x; -- fail
+
+drop function anyctest(anycompatiblemultirange, anycompatiblemultirange);
+
+-- fail, can't infer result type:
+create function anyctest(anycompatible)
+returns anycompatiblemultirange as $$
+ select $1
+$$ language sql;
+
create function anyctest(anycompatiblenonarray, anycompatiblenonarray)
returns anycompatiblearray as $$
select array[$1, $2]
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 4048b1d185..0276d19136 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -528,6 +532,7 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
@@ -539,6 +544,13 @@ create function outparam2_succeed(r anyrange, out lu anyarray, out ul anyarray)
select * from outparam2_succeed(int4range(1,11));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index 4b492ce062..07879d9a57 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -153,7 +153,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -182,7 +182,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -234,7 +234,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -263,7 +263,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -467,3 +467,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 525d58e7f0..0b6a0717e9 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1362,6 +1362,9 @@ MultiXactMember
MultiXactOffset
MultiXactStateData
MultiXactStatus
+MultirangeIOData
+MultirangeParseState
+MultirangeType
MyData
NDBOX
NODE
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-14 18:13 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-17 04:52 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-19 20:41 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-19 20:43 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-20 05:24 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-23 23:32 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-24 04:23 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2020-04-04 23:10 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-04-11 03:44 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-04-11 16:36 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-04-11 16:42 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-07-05 17:20 ` Re: range_agg Justin Pryzby <pryzby@telsasoft.com>
0 siblings, 2 replies; 152+ messages in thread
From: Paul A Jungwirth @ 2020-04-11 16:36 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
On Fri, Apr 10, 2020 at 8:44 PM Paul A Jungwirth
<pj@illuminatedcomputing.com> wrote:
> Thanks, and thanks for your v17 also. Here is a patch building on that
> and adding support for anycompatiblemultirange.
Here is a v19 that just moved the multirange tests to a new parallel
group to avoid a max-20-tests error. Sorry about that!
Btw I'm working on typanalyze + selectivity, and it seems like the
test suite doesn't run those things? At least I can't seem to get it
to call the existing range typanalyze functions. Those would run from
the vacuum process, not an ordinary backend, right? Is there a
separate test suite for that I'm overlooking? I'm sure I can figure it
out, but since I'm uploading a new patch file I thought I'd ask. . . .
Thanks,
Paul
Attachments:
[application/octet-stream] v19-multirange.patch (364.5K, ../../CA+renyXVz0hUmqhD5iHUZixcpNm3wrBVFqV3ewTPEe7vx9EBGg@mail.gmail.com/2-v19-multirange.patch)
download | inline diff:
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index c2e42f31c0..22404d18cd 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4812,6 +4812,10 @@ SELECT * FROM pg_attribute
</indexterm>
<indexterm zone="datatype-pseudo">
+ <primary>anymultirange</primary>
+ </indexterm>
+
+ <indexterm zone="datatype-pseudo">
<primary>anycompatible</primary>
</indexterm>
@@ -4828,6 +4832,10 @@ SELECT * FROM pg_attribute
</indexterm>
<indexterm zone="datatype-pseudo">
+ <primary>anycompatiblemultirange</primary>
+ </indexterm>
+
+ <indexterm zone="datatype-pseudo">
<primary>void</primary>
</indexterm>
@@ -4937,6 +4945,13 @@ SELECT * FROM pg_attribute
</row>
<row>
+ <entry><type>anymultirange</type></entry>
+ <entry>Indicates that a function accepts any multirange data type
+ (see <xref linkend="extend-types-polymorphic"/> and
+ <xref linkend="rangetypes"/>).</entry>
+ </row>
+
+ <row>
<entry><type>anycompatible</type></entry>
<entry>Indicates that a function accepts any data type,
with automatic promotion of multiple arguments to a common data type
@@ -4966,6 +4981,14 @@ SELECT * FROM pg_attribute
</row>
<row>
+ <entry><type>anycompatiblemultirange</type></entry>
+ <entry>Indicates that a function accepts any multirange data type,
+ with automatic promotion of multiple arguments to a common data type
+ (see <xref linkend="extend-types-polymorphic"/> and
+ <xref linkend="rangetypes"/>).</entry>
+ </row>
+
+ <row>
<entry><type>cstring</type></entry>
<entry>Indicates that a function accepts or returns a null-terminated C string.</entry>
</row>
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 930aeb767c..3f62c06daf 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -286,6 +286,14 @@
</row>
<row>
+ <entry><type>anymultirange</type></entry>
+ <entry>Simple</entry>
+ <entry>Indicates that a function accepts any multirange data type
+ (see <xref linkend="rangetypes"/>)
+ </entry>
+ </row>
+
+ <row>
<entry><type>anycompatible</type></entry>
<entry>Common</entry>
<entry>Indicates that a function accepts any data type,
@@ -316,6 +324,14 @@
with automatic promotion of multiple arguments to a common data type
</entry>
</row>
+
+ <row>
+ <entry><type>anycompatiblemultirange</type></entry>
+ <entry>Common</entry>
+ <entry>Indicates that a function accepts any multirange data type,
+ with automatic promotion of multiple arguments to a common data type
+ </entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -343,17 +359,15 @@
position declared as <type>anyarray</type> can have any array data type,
but similarly they must all be the same type. And similarly,
positions declared as <type>anyrange</type> must all be the same range
- type. Furthermore, if there are
+ type. Likewise for <type>anymultirange</type>.
+ </para>
+
+ <para>
+ Furthermore, if there are
positions declared <type>anyarray</type> and others declared
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
- Similarly, if there are positions declared <type>anyrange</type>
- and others declared <type>anyelement</type> or <type>anyarray</type>,
- the actual range type in the <type>anyrange</type> positions must be a
- range whose subtype is the same type appearing in
- the <type>anyelement</type> positions and the same as the element type
- of the <type>anyarray</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@@ -363,6 +377,19 @@
</para>
<para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</type> or <type>anyarray</type>,
+ the actual range type in the <type>anyrange</type> positions must be a
+ range whose subtype is the same type appearing in
+ the <type>anyelement</type> positions and the same as the element type
+ of the <type>anyarray</type> positions.
+ If there are positions declared <type>anymultirange</type>,
+ their actual multirange type must contain ranges matching parameters declared
+ <type>anyrange</type> and base elements matching parameters declared
+ <type>anyelement</type> and <type>anyarray</type>.
+ </para>
+
+ <para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
types are allowed. For example, a function declared as
@@ -417,7 +444,8 @@
Selection of the common type considers the actual types
of <type>anycompatible</type> and <type>anycompatiblenonarray</type>
inputs, the array element types of <type>anycompatiblearray</type>
- inputs, and the range subtypes of <type>anycompatiblerange</type>
+ inputs, the range subtypes of <type>anycompatiblerange</type> inputs,
+ and the multirange subtypes of <type>anycompatiablemultirange</type>
inputs. If <type>anycompatiblenonarray</type> is present then the
common type is required to be a non-array type. Once a common type is
identified, arguments in <type>anycompatible</type>
@@ -428,12 +456,15 @@
<para>
Since there is no way to select a range type knowing only its subtype,
- use of <type>anycompatiblerange</type> requires that all arguments
- declared with that type have the same actual range type, and that that
- type's subtype agree with the selected common type, so that no casting
- of the range values is required. As with <type>anyrange</type>, use
- of <type>anycompatiblerange</type> as a function result type requires
- that there be an <type>anycompatiblerange</type> argument.
+ use of <type>anycompatiblerange</type> and/or
+ <type>anycompatiblemultirange</type> requires that all arguments declared
+ with that type have the same actual range and/or multirange type, and that
+ that type's subtype agree with the selected common type, so that no casting
+ of the range values is required. As with <type>anyrange</type> and
+ <type>anymultirange</type>, use of <type>anycompatiblerange</type> and
+ <type>anymultirange</type> as a function result type requires that there be
+ an <type>anycompatiblerange</type> or <type>anycompatiblemultirange</type>
+ argument.
</para>
<para>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 12d75b476f..2e7c62f1a0 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -13609,7 +13609,7 @@ SELECT NULLIF(value, '(none)') ...
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14080,12 +14080,14 @@ NULL baz</literallayout>(3 rows)</entry>
<title>Range Functions and Operators</title>
<para>
- See <xref linkend="rangetypes"/> for an overview of range types.
+ See <xref linkend="rangetypes"/> for an overview of range and multirange types.
</para>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14103,134 +14105,215 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
- <entry><literal>f</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">f</literallayout></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
- <entry><literal>[5,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)
+'{[5,10)}'::nummultirange + '{[15,20)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,20)
+{[5,10), [15,20)}</literallayout></entry>
</row>
<row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
- <entry><literal>[10,15)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)
+'{[5,15)}'::int8multirange * '{[10,20)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[10,15)
+{[10,15)}</literallayout></entry>
</row>
<row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
- <entry><literal>[5,10)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)
+'{[5,20)}'::int8multirange - '{[10,15)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,10)
+{[5,10), [15,20)}</literallayout></entry>
</row>
</tbody>
@@ -14248,19 +14331,31 @@ NULL baz</literallayout>(3 rows)</entry>
<para>
The left-of/right-of/adjacent operators always return false when an empty
- range is involved; that is, an empty range is not considered to be either
- before or after any other range.
+ range or multirange is involved; that is, an empty range is not considered to
+ be either before or after any other range.
+ </para>
+
+ <para>
+ Elsewhere empty ranges and multiranges are treated as the additive identity:
+ anything unioned with an empty value is itself. Anything minus an empty
+ value is itself. An empty multirange has exactly the same points as an empty
+ range. Every range contains the empty range. Every multirange contains as many
+ empty ranges as you like.
</para>
<para>
The union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
- represented.
+ represented. There are separate operators for union and difference that take
+ multirange parameters and return a multirange, and they do not fail even if
+ their arguments are disjoint. So if you need a union or difference operation
+ for ranges that may be disjoint, you can avoid errors by first casting your
+ ranges to multiranges.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
- available for use with range types.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14312,6 +14407,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14323,6 +14429,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>isempty</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14334,6 +14451,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14345,6 +14473,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inc</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14356,6 +14495,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>lower_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14367,6 +14517,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>upper_inf</function>(<type>anyrange</type>)
</literal>
</entry>
@@ -14378,6 +14539,17 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
<function>range_merge</function>(<type>anyrange</type>, <type>anyrange</type>)
</literal>
</entry>
@@ -14386,16 +14558,38 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
+ <function>multirange</function>(<type>anyrange</type>)
+ </literal>
+ </entry>
+ <entry><type>anymultirange</type></entry>
+ <entry>a multirange containing just the given range</entry>
+ <entry><literal>multirange('[1,2)'::int4range)</literal></entry>
+ <entry><literal>{[1,2)}</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
- functions all return false for an empty range.
+ functions all return false for an empty range or multirange.
</para>
</sect1>
@@ -14717,6 +14911,44 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>string_agg</primary>
</indexterm>
<function>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index b75fb3a392..c8784bdce3 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
ranges from an instrument, and so forth can also be useful.
</para>
+ <para>
+ Every range type has a corresponding multirange type. A multirange is
+ an ordered list of non-continguous, non-empty, non-null ranges. Most
+ range operators also work on multiranges, and they have a few functions
+ of their own.
+ </para>
+
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
@@ -232,10 +239,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
+
+ <para>
+ The input for a multirange is curly brackets (<literal>{</literal> and
+ <literal>}</literal>) containing zero or more valid ranges,
+ separated by commas. Whitespace is permitted around the brackets and
+ commas. This is intended to be reminiscent of array syntax, although
+ multiranges are much simpler: they have just one dimension and there is
+ no need to quote their contents. (The bounds of their ranges may be
+ quoted as above however.)
+ </para>
+
+ <para>
+ Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+ </para>
+
</sect2>
<sect2 id="rangetypes-construct">
- <title>Constructing Ranges</title>
+ <title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@@ -269,6 +296,19 @@ SELECT int8range(1, 14, '(]');
SELECT numrange(NULL, 2.2);
</programlisting>
</para>
+
+ <para>
+ Each range type also has a multirange constructor with the same name as the
+ multirange type. The constructor function takes zero or more arguments
+ which are all ranges of the appropriate type.
+ For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
+</programlisting>
+ </para>
</sect2>
<sect2 id="rangetypes-discrete">
@@ -342,6 +382,11 @@ SELECT '[1.234, 5.678]'::floatrange;
</para>
<para>
+ When you define your own range you automatically get a corresponding
+ multirange type.
+ </para>
+
+ <para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort
ordering that determines which values fall into a given range.
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index b5bc36c2bd..1217a6ddd0 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff)
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
+ ObjectAddress referencing;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+ values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@@ -100,6 +102,12 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* record multirange type's dependency on the range type */
+ referencing.classId = TypeRelationId;
+ referencing.objectId = multirangeTypeOid;
+ referencing.objectSubId = 0;
+ recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
table_close(pg_range, RowExclusiveLock);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index cd56714968..c359f27c74 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -27,6 +27,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
@@ -36,6 +37,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
+static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
+ bool tryOriginal);
+
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -809,31 +813,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
char *
makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
- char *arr = (char *) palloc(NAMEDATALEN);
- int namelen = strlen(typeName);
- int i;
-
- /*
- * The idea is to prepend underscores as needed until we make a name that
- * doesn't collide with anything...
- */
- for (i = 1; i < NAMEDATALEN - 1; i++)
- {
- arr[i - 1] = '_';
- if (i + namelen < NAMEDATALEN)
- strcpy(arr + i, typeName);
- else
- {
- memcpy(arr + i, typeName, NAMEDATALEN - i);
- truncate_identifier(arr, NAMEDATALEN, false);
- }
- if (!SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(arr),
- ObjectIdGetDatum(typeNamespace)))
- break;
- }
+ char *arr;
- if (i >= NAMEDATALEN - 1)
+ arr = makeUniqueTypeName(typeName, typeNamespace, false);
+ if (arr == NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("could not form array type name for type \"%s\"",
@@ -904,3 +887,91 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
+
+
+/*
+ * makeMultirangeTypeName
+ * - given a range type name, make a multirange type name for it
+ *
+ * caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+ char *buf;
+ char *mrname;
+ char *rangestr;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "_multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ rangestr = strstr(rangeTypeName, "range");
+ if (rangestr)
+ {
+ char *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
+
+ buf = psprintf("%s%s%s", prefix, "multi", rangestr);
+ }
+ else
+ buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
+
+ /* clip it at NAMEDATALEN-1 bytes */
+ buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
+
+ mrname = makeUniqueTypeName(buf, typeNamespace, true);
+ if (mrname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mrname;
+}
+
+/*
+ * makeUniqueTypeName
+ * Generate a unique name for a prospective new type
+ *
+ * Given a typeName, return a new palloc'ed name by preprending underscores
+ * until a non-conflicting name results.
+ *
+ * If tryOriginal, first try with zero underscores.
+ */
+static char *
+makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
+{
+ int i;
+ int namelen;
+ char dest[NAMEDATALEN];
+
+ Assert(strlen(typeName) <= NAMEDATALEN - 1);
+
+ if (tryOriginal &&
+ !SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(typeName);
+
+ /*
+ * The idea is to prepend underscores as needed until we make a name that
+ * doesn't collide with anything ...
+ */
+ namelen = strlen(typeName);
+ for (i = 1; i < NAMEDATALEN - 1; i++)
+ {
+ dest[i - 1] = '_';
+ strlcpy(dest + i, typeName, NAMEDATALEN - i);
+ if (namelen + i >= NAMEDATALEN)
+ truncate_identifier(dest, NAMEDATALEN, false);
+
+ if (!SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(dest),
+ ObjectIdGetDatum(typeNamespace)))
+ return pstrdup(dest);
+ }
+
+ return NULL;
+}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 8891b1d564..dfb2edd3d0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -105,9 +105,14 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid,
+ Oid rangeArrayOid, Oid *castFuncOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -738,7 +743,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
- typtype != TYPTYPE_RANGE)
+ typtype != TYPTYPE_RANGE &&
+ typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@@ -1279,6 +1285,11 @@ checkEnumOwner(HeapTuple tup)
/*
* DefineRange
* Registers a new range type.
+ *
+ * Perhaps it might be worthwhile to set pg_type.typelem to the base type,
+ * and likewise on multiranges to set it to the range type. But having a
+ * non-zero typelem is treated elsewhere as a synonym for being an array,
+ * and users might have queries with that same assumption.
*/
ObjectAddress
DefineRange(CreateRangeStmt *stmt)
@@ -1287,7 +1298,11 @@ DefineRange(CreateRangeStmt *stmt)
Oid typeNamespace;
Oid typoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1304,6 +1319,8 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
+ ObjectAddress mltrngaddress;
+ Oid castFuncOid;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1452,8 +1469,10 @@ DefineRange(CreateRangeStmt *stmt)
/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for ranges */
alignment = (subtypalign == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
- /* Allocate OID for array type */
+ /* Allocate OID for array type, its multirange, and its multirange array */
rangeArrayOid = AssignTypeArrayOid();
+ multirangeOid = AssignTypeMultirangeOid();
+ multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
address =
@@ -1491,9 +1510,46 @@ DefineRange(CreateRangeStmt *stmt)
Assert(typoid == InvalidOid || typoid == address.objectId);
typoid = address.objectId;
+ /* Create the multirange that goes with it */
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(multirangeOid, /* force assignment of this type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
+ TYPCATEGORY_RANGE, /* type-category (range type) */
+ false, /* multirange types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_MULTIRANGE_IN, /* input procedure */
+ F_MULTIRANGE_OUT, /* output procedure */
+ F_MULTIRANGE_RECV, /* receive procedure */
+ F_MULTIRANGE_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
+ false, /* this is not an array type */
+ multirangeArrayOid, /* array type we are about to create */
+ InvalidOid, /* base type ID (only for domains) */
+ NULL, /* never a default type value */
+ NULL, /* no binary form available either */
+ false, /* never passed by value */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* type's collation (ranges never have one) */
+ Assert(multirangeOid == mltrngaddress.objectId);
+
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
- rangeCanonical, rangeSubtypeDiff);
+ rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
@@ -1534,8 +1590,53 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* Create the multirange's array type */
+
+ multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+ TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
+ multirangeArrayName, /* type name */
+ typeNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
+ TYPTYPE_BASE, /* type-type (base type) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
+ false, /* array types are never preferred */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* typmodin procedure - none */
+ InvalidOid, /* typmodout procedure - none */
+ F_ARRAY_TYPANALYZE, /* analyze procedure */
+ multirangeOid, /* element type ID */
+ true, /* yes this is an array type */
+ InvalidOid, /* no further array type */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ alignment, /* alignment - same as range's */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
+
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+ makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+ multirangeOid, typoid, rangeArrayOid,
+ &castFuncOid);
+
+ /* Create cast from the range type to its multirange type */
+ CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
+
+ pfree(multirangeTypeName);
+ pfree(multirangeArrayName);
return address;
}
@@ -1613,6 +1714,147 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ *
+ * Sets castFuncOid to the oid of the new constructor that can be used
+ * to cast from a range to a multirange.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+ Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
+ Oid *castFuncOid)
+{
+ ObjectAddress myself,
+ referenced;
+ oidvector *argtypes;
+ Datum allParamTypes;
+ ArrayType *allParameterTypes;
+ Datum paramModes;
+ ArrayType *parameterModes;
+
+ referenced.classId = TypeRelationId;
+ referenced.objectId = multirangeOid;
+ referenced.objectSubId = 0;
+
+ /* 0-arg constructor - for empty multiranges */
+ argtypes = buildoidvector(NULL, 0);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor0", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /*
+ * Make the constructor internally-dependent on the multirange type so
+ * that they go away silently when the type is dropped. Note that pg_dump
+ * depends on this choice to avoid dumping the constructors.
+ */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+
+ /*
+ * 1-arg constructor - for casts
+ *
+ * In theory we shouldn't need both this and the vararg (n-arg) constructor,
+ * but having a separate 1-arg function lets us define casts against it.
+ */
+ argtypes = buildoidvector(&rangeOid, 1);
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor1", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ true, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(NULL), /* allParameterTypes */
+ PointerGetDatum(NULL), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ *castFuncOid = myself.objectId;
+
+ /* n-arg constructor - vararg */
+ argtypes = buildoidvector(&rangeArrayOid, 1);
+ allParamTypes = ObjectIdGetDatum(rangeArrayOid);
+ allParameterTypes = construct_array(&allParamTypes,
+ 1, OIDOID,
+ sizeof(Oid), true, 'i');
+ paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
+ parameterModes = construct_array(¶mModes, 1, CHAROID,
+ 1, true, 'c');
+ myself = ProcedureCreate(name, /* name: same as multirange type */
+ namespace,
+ false, /* replace */
+ false, /* returns set */
+ multirangeOid, /* return type */
+ BOOTSTRAP_SUPERUSERID, /* proowner */
+ INTERNALlanguageId, /* language */
+ F_FMGR_INTERNAL_VALIDATOR,
+ "multirange_constructor2", /* prosrc */
+ NULL, /* probin */
+ PROKIND_FUNCTION,
+ false, /* security_definer */
+ false, /* leakproof */
+ false, /* isStrict */
+ PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
+ argtypes, /* parameterTypes */
+ PointerGetDatum(allParameterTypes), /* allParameterTypes */
+ PointerGetDatum(parameterModes), /* parameterModes */
+ PointerGetDatum(NULL), /* parameterNames */
+ NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
+ PointerGetDatum(NULL), /* proconfig */
+ InvalidOid, /* prosupport */
+ 1.0, /* procost */
+ 0.0); /* prorows */
+ /* ditto */
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ pfree(argtypes);
+ pfree(allParameterTypes);
+ pfree(parameterModes);
+}
/*
* Find suitable I/O functions for a type.
@@ -2045,6 +2287,72 @@ AssignTypeArrayOid(void)
return type_array_oid;
}
+/*
+ * AssignTypeMultirangeOid
+ *
+ * Pre-assign the range type's multirange OID for use in pg_type.oid
+ */
+Oid
+AssignTypeMultirangeOid(void)
+{
+ Oid type_multirange_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
+
+ type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
+ binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_oid;
+}
+
+/*
+ * AssignTypeMultirangeArrayOid
+ *
+ * Pre-assign the range type's multirange array OID for use in pg_type.typarray
+ */
+Oid
+AssignTypeMultirangeArrayOid(void)
+{
+ Oid type_multirange_array_oid;
+
+ /* Use binary-upgrade override for pg_type.oid? */
+ if (IsBinaryUpgrade)
+ {
+ if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
+
+ type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
+ binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
+ }
+ else
+ {
+ Relation pg_type = table_open(TypeRelationId, AccessShareLock);
+
+ type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+ Anum_pg_type_oid);
+ table_close(pg_type, AccessShareLock);
+ }
+
+ return type_multirange_array_oid;
+}
+
/*-------------------------------------------------------------------
* DefineCompositeType
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 1c387a952e..e26822361c 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -1693,7 +1693,8 @@ check_sql_fn_retval(List *queryTreeList,
if (fn_typtype == TYPTYPE_BASE ||
fn_typtype == TYPTYPE_DOMAIN ||
fn_typtype == TYPTYPE_ENUM ||
- fn_typtype == TYPTYPE_RANGE)
+ fn_typtype == TYPTYPE_RANGE ||
+ fn_typtype == TYPTYPE_MULTIRANGE)
{
/*
* For scalar-type returns, the target list must have exactly one
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 1b11cf731c..04cbffd7c2 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -189,19 +189,21 @@ coerce_type(ParseState *pstate, Node *node,
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID ||
targetTypeId == ANYCOMPATIBLEARRAYOID ||
- targetTypeId == ANYCOMPATIBLERANGEOID)
+ targetTypeId == ANYCOMPATIBLERANGEOID ||
+ targetTypeId == ANYCOMPATIBLEMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
- * the argument must be an actual array, enum, or range type. In
- * particular the argument must *not* be an UNKNOWN constant. If it
- * is, we just fall through; below, we'll call the pseudotype's input
- * function, which will produce an error. Also, if what we have is a
- * domain over array, enum, or range, we have to relabel it to its
- * base type.
+ * the argument must be an actual array, enum, range, or multirange
+ * type. In particular the argument must *not* be an UNKNOWN
+ * constant. If it is, we just fall through; below, we'll call
+ * the pseudotype's input function, which will produce an error. Also,
+ * if what we have is a domain over array, enum, range, or multirange,
+ * we have to relabel it to its base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@@ -1532,8 +1534,8 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* 1) All arguments declared ANYELEMENT must have the same datatype.
* 2) All arguments declared ANYARRAY must have the same datatype,
* which must be a varlena array type.
- * 3) All arguments declared ANYRANGE must have the same datatype,
- * which must be a range type.
+ * 3) All arguments declared ANYRANGE or ANYMULTIRANGE must be a range or
+ * multirange type, all derived from the same base datatype.
* 4) If there are arguments of more than one of these polymorphic types,
* the array element type and/or range subtype must be the same as each
* other and the same as the ANYELEMENT type.
@@ -1548,8 +1550,8 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* to a common supertype (chosen as per select_common_type's rules).
* ANYCOMPATIBLENONARRAY works like ANYCOMPATIBLE but also requires the
* common supertype to not be an array. If there are ANYCOMPATIBLEARRAY
- * or ANYCOMPATIBLERANGE arguments, their element types or subtypes are
- * included while making the choice of common supertype.
+ * or ANYCOMPATIBLERANGE or ANYCOMPATIBLEMULTIRANGE arguments, their element
+ * types or subtypes are included while making the choice of common supertype.
* 8) The resolved type of ANYCOMPATIBLEARRAY arguments will be the array
* type over the common supertype (which might not be the same array type
* as any of the original arrays).
@@ -1557,6 +1559,10 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* (after domain flattening), since we have no preference rule that would
* let us choose one over another. Furthermore, that range's subtype
* must exactly match the common supertype chosen by rule 7.
+ * 10) All ANYCOMPATIBLEMULTIRANGE arguments must be the exact same multirange
+ * type (after domain flattening), since we have no preference rule that would
+ * let us choose one over another. Furthermore, that multirange's range's
+ * subtype must exactly match the common supertype chosen by rule 7.
*
* Domains over arrays match ANYARRAY, and are immediately flattened to their
* base type. (Thus, for example, we will consider it a match if one ANYARRAY
@@ -1565,7 +1571,9 @@ coerce_to_common_type(ParseState *pstate, Node *node,
* for ANYCOMPATIBLEARRAY and ANYCOMPATIBLENONARRAY.
*
* Similarly, domains over ranges match ANYRANGE or ANYCOMPATIBLERANGE,
- * and are immediately flattened to their base type.
+ * and are immediately flattened to their base type, and domains over
+ * multiranges match ANYMULTIRANGE or ANYCOMPATIBLEMULTIRANGE and are immediately
+ * flattened to their base type.
*
* Note that domains aren't currently considered to match ANYENUM,
* even if their base type would match.
@@ -1583,8 +1591,12 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid anycompatible_range_typeid = InvalidOid;
Oid anycompatible_range_typelem = InvalidOid;
+ Oid anycompatible_multirange_typeid = InvalidOid;
+ Oid anycompatible_multirange_typelem = InvalidOid;
+ Oid range_typelem = InvalidOid;
bool have_anynonarray = false;
bool have_anyenum = false;
bool have_anycompatible_nonarray = false;
@@ -1633,6 +1645,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ return false;
+ multirange_typeid = actual_type;
+ }
else if (decl_type == ANYCOMPATIBLEOID ||
decl_type == ANYCOMPATIBLENONARRAYOID)
{
@@ -1677,6 +1698,42 @@ check_generic_type_consistency(const Oid *actual_arg_types,
anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
}
}
+ else if (decl_type == ANYCOMPATIBLEMULTIRANGEOID)
+ {
+ if (actual_type == UNKNOWNOID)
+ continue;
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(anycompatible_multirange_typeid))
+ {
+ /* All ANYCOMPATIBLEMULTIRANGE arguments must be the same type */
+ if (anycompatible_multirange_typeid != actual_type)
+ return false;
+ }
+ else
+ {
+ anycompatible_multirange_typeid = actual_type;
+ anycompatible_multirange_typelem = get_range_multirange_subtype(actual_type);
+ if (!OidIsValid(anycompatible_multirange_typelem))
+ return false; /* not a multirange type */
+
+ if (OidIsValid(anycompatible_range_typeid))
+ {
+ /* ANYCOMPATIBLEMULTIRANGE and ANYCOMPATIBLERANGE arguments must match */
+ if (anycompatible_range_typeid != anycompatible_multirange_typelem)
+ return false;
+ }
+ else
+ {
+ anycompatible_range_typeid = anycompatible_multirange_typelem;
+ anycompatible_range_typelem = get_range_subtype(anycompatible_range_typeid);
+ if (!OidIsValid(anycompatible_range_typelem))
+ return false; /* not a range type */
+ }
+ /* collect the subtype for common-supertype choice */
+ anycompatible_actual_types[n_anycompatible_args++] =
+ anycompatible_range_typelem;
+ }
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1723,8 +1780,6 @@ check_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- Oid range_typelem;
-
range_typelem = get_range_subtype(range_typeid);
if (!OidIsValid(range_typelem))
return false; /* should be a range, but isn't */
@@ -1743,6 +1798,45 @@ check_generic_type_consistency(const Oid *actual_arg_types,
}
}
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ Oid multirange_typelem;
+
+ multirange_typelem = get_range_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ return false; /* should be a multirange, but isn't */
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(multirange_typelem);
+ if (!OidIsValid(range_typelem))
+ return false; /* should be a range, but isn't */
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * If we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ return false;
+ }
+ }
+
if (have_anynonarray)
{
/* require the element type to not be an array or domain over array */
@@ -1781,8 +1875,10 @@ check_generic_type_consistency(const Oid *actual_arg_types,
}
/*
- * the anycompatible type must exactly match the range element type,
- * if we were able to identify one
+ * The anycompatible type must exactly match the range element type,
+ * if we were able to identify one. This checks compatibility for
+ * anycompatiblemultirange too since that also sets
+ * anycompatible_range_typelem above.
*/
if (OidIsValid(anycompatible_range_typelem) &&
anycompatible_range_typelem != anycompatible_typeid)
@@ -1821,21 +1917,27 @@ check_generic_type_consistency(const Oid *actual_arg_types,
* argument's actual type as the function's return type.
* 2) If return type is ANYARRAY, and any argument is ANYARRAY, use the
* argument's actual type as the function's return type.
- * 3) Similarly, if return type is ANYRANGE, and any argument is ANYRANGE,
- * use the argument's actual type as the function's return type.
- * 4) Otherwise, if return type is ANYELEMENT or ANYARRAY, and there is
+ * 3) Similarly, if return type is ANYRANGE or ANYMULTIRANGE, and any
+ * argument is ANYRANGE or ANYMULTIRANGE, use that argument's
+ * actual type, range type or multirange type as the function's return
+ * type.
+ * 4) Otherwise, if return type is ANYMULTIRANGE, and any argument is
+ * ANYMULTIRANGE, use the argument's actual type as the function's return
+ * type. Or if any argument is ANYRANGE, use its multirange type as the
+ * function's return type.
+ * 5) Otherwise, if return type is ANYELEMENT or ANYARRAY, and there is
* at least one ANYELEMENT, ANYARRAY, or ANYRANGE input, deduce the
* return type from those inputs, or throw error if we can't.
- * 5) Otherwise, if return type is ANYRANGE, throw error. (We have no way to
- * select a specific range type if the arguments don't include ANYRANGE.)
- * 6) ANYENUM is treated the same as ANYELEMENT except that if it is used
+ * 6) Otherwise, if return type is ANYRANGE or ANYMULTIRANGE, throw error.
+ * (We have no way to select a specific range type if the arguments don't
+ * include ANYRANGE.)
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
- * 7) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
+ * 8) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
* we add the extra condition that the ANYELEMENT type must not be an array.
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
* is an extra restriction if not.)
- * 8) ANYCOMPATIBLE, ANYCOMPATIBLEARRAY, ANYCOMPATIBLENONARRAY, and
+ * 9) ANYCOMPATIBLE, ANYCOMPATIBLEARRAY, ANYCOMPATIBLENONARRAY, and
* ANYCOMPATIBLERANGE are handled by resolving the common supertype
* of those arguments (or their element types/subtypes, for array and range
* inputs), and then coercing all those arguments to the common supertype,
@@ -1889,15 +1991,21 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
+ Oid multirange_typeid = InvalidOid;
Oid anycompatible_typeid = InvalidOid;
Oid anycompatible_array_typeid = InvalidOid;
Oid anycompatible_range_typeid = InvalidOid;
Oid anycompatible_range_typelem = InvalidOid;
+ Oid anycompatible_multirange_typeid = InvalidOid;
+ Oid anycompatible_multirange_typelem = InvalidOid;
+ Oid range_typelem;
+ Oid multirange_typelem;
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
bool have_anycompatible_nonarray = (rettype == ANYCOMPATIBLENONARRAYOID);
bool have_anycompatible_array = (rettype == ANYCOMPATIBLEARRAYOID);
bool have_anycompatible_range = (rettype == ANYCOMPATIBLERANGEOID);
+ bool have_anycompatible_multirange = (rettype == ANYCOMPATIBLEMULTIRANGEOID);
int n_poly_args = 0; /* this counts all family-1 arguments */
int n_anycompatible_args = 0; /* this counts only non-unknowns */
Oid anycompatible_actual_types[FUNC_MAX_ARGS];
@@ -1977,6 +2085,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ n_poly_args++;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_poly_unknowns = true;
+ continue;
+ }
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anymultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(actual_type))));
+ multirange_typeid = actual_type;
+ }
else if (decl_type == ANYCOMPATIBLEOID ||
decl_type == ANYCOMPATIBLENONARRAYOID)
{
@@ -2045,6 +2173,41 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
}
}
+ else if (decl_type == ANYCOMPATIBLEMULTIRANGEOID)
+ {
+ have_poly_anycompatible = true;
+ have_anycompatible_multirange = true;
+ if (actual_type == UNKNOWNOID)
+ continue;
+ if (allow_poly && decl_type == actual_type)
+ continue; /* no new information here */
+ actual_type = getBaseType(actual_type); /* flatten domains */
+ if (OidIsValid(anycompatible_multirange_typeid))
+ {
+ /* All ANYCOMPATIBLEMULTIRANGE arguments must be the same type */
+ if (anycompatible_multirange_typeid != actual_type)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("arguments declared \"anycompatiblemultirange\" are not all alike"),
+ errdetail("%s versus %s",
+ format_type_be(anycompatible_multirange_typeid),
+ format_type_be(actual_type))));
+ }
+ else
+ {
+ anycompatible_multirange_typeid = actual_type;
+ anycompatible_multirange_typelem = get_range_multirange_subtype(actual_type);
+ anycompatible_range_typelem = get_range_subtype(anycompatible_multirange_typelem);
+ if (!OidIsValid(anycompatible_multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anycompatiblemultirange",
+ format_type_be(actual_type))));
+ /* collect the subtype for common-supertype choice */
+ anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
+ }
+ }
}
/*
@@ -2113,8 +2276,6 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
- Oid range_typelem;
-
range_typelem = get_range_subtype(range_typeid);
if (!OidIsValid(range_typelem))
ereport(ERROR,
@@ -2143,6 +2304,61 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(elem_typeid))));
}
}
+ else
+ range_typelem = InvalidOid;
+
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_range_multirange_subtype(multirange_typeid);
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_typeid))));
+
+ if (!OidIsValid(range_typeid))
+ {
+ /*
+ * If we don't have a range type yet, use the one we just got
+ */
+ range_typeid = multirange_typelem;
+ range_typelem = get_range_subtype(range_typeid);
+ }
+ else if (multirange_typelem != range_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyrange"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(range_typeid))));
+ }
+
+ if (!OidIsValid(elem_typeid))
+ {
+ /*
+ * if we don't have an element type yet, use the one we just got
+ */
+ elem_typeid = range_typelem;
+ }
+ else if (range_typelem != elem_typeid)
+ {
+ /* otherwise, they better match */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not consistent with argument declared %s",
+ "anymultirange", "anyelement"),
+ errdetail("%s versus %s",
+ format_type_be(multirange_typeid),
+ format_type_be(elem_typeid))));
+ }
+ }
+ else
+ multirange_typelem = InvalidOid;
if (!OidIsValid(elem_typeid))
{
@@ -2151,6 +2367,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -2250,6 +2467,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
anycompatible_typeid = ANYCOMPATIBLEOID;
anycompatible_array_typeid = ANYCOMPATIBLEARRAYOID;
anycompatible_range_typeid = ANYCOMPATIBLERANGEOID;
+ anycompatible_multirange_typeid = ANYCOMPATIBLEMULTIRANGEOID;
}
else
{
@@ -2281,6 +2499,8 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
declared_arg_types[j] = anycompatible_array_typeid;
else if (decl_type == ANYCOMPATIBLERANGEOID)
declared_arg_types[j] = anycompatible_range_typeid;
+ else if (decl_type == ANYCOMPATIBLEMULTIRANGEOID)
+ declared_arg_types[j] = anycompatible_multirange_typeid;
}
}
@@ -2331,6 +2551,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ declared_arg_types[j] = multirange_typeid;
+ }
}
}
@@ -2367,6 +2598,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
+ /* if we return ANYMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYMULTIRANGEOID)
+ {
+ if (!OidIsValid(multirange_typeid))
+ {
+ if (OidIsValid(range_typeid))
+ multirange_typeid = get_range_multirange(range_typeid);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(elem_typeid))));
+ }
+ return multirange_typeid;
+ }
+
/* if we return ANYCOMPATIBLE use the appropriate type */
if (rettype == ANYCOMPATIBLEOID ||
rettype == ANYCOMPATIBLENONARRAYOID)
@@ -2401,6 +2648,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return anycompatible_range_typeid;
}
+ /* if we return ANYCOMPATIBLEMULTIRANGE use the appropriate argument type */
+ if (rettype == ANYCOMPATIBLEMULTIRANGEOID)
+ {
+ /* this error is unreachable if the function signature is valid: */
+ if (!OidIsValid(anycompatible_multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg_internal("could not identify anycompatiblemultirange type")));
+ return anycompatible_multirange_typeid;
+ }
+
/* we don't return a generic type; send back the original return type */
return rettype;
}
@@ -2418,20 +2676,37 @@ check_valid_polymorphic_signature(Oid ret_type,
const Oid *declared_arg_types,
int nargs)
{
- if (ret_type == ANYRANGEOID || ret_type == ANYCOMPATIBLERANGEOID)
+ if (ret_type == ANYRANGEOID || ret_type == ANYMULTIRANGEOID)
{
/*
- * ANYRANGE requires an ANYRANGE input, else we can't tell which of
- * several range types with the same element type to use. Likewise
- * for ANYCOMPATIBLERANGE.
+ * ANYRANGE and ANYMULTIRANGE require an ANYRANGE or ANYMULTIRANGE input,
+ * else we can't tell which of several range types with the same element
+ * type to use.
*/
for (int i = 0; i < nargs; i++)
{
- if (declared_arg_types[i] == ret_type)
+ if (declared_arg_types[i] == ANYRANGEOID ||
+ declared_arg_types[i] == ANYMULTIRANGEOID)
return NULL; /* OK */
}
- return psprintf(_("A result of type %s requires at least one input of type %s."),
- format_type_be(ret_type), format_type_be(ret_type));
+ return psprintf(_("A result of type %s requires at least one input of type anyrange or anymultirange."),
+ format_type_be(ret_type));
+ }
+ else if (ret_type == ANYCOMPATIBLERANGEOID || ret_type == ANYCOMPATIBLEMULTIRANGEOID)
+ {
+ /*
+ * ANYCOMPATIBLERANGE and ANYCOMPATIBLEMULTIRANGE require an ANYCOMPATIBLERANGE or
+ * ANYCOMPATIBLEMULTIRANGE input, else we can't tell which of several range types
+ * with the same element type to use.
+ */
+ for (int i = 0; i < nargs; i++)
+ {
+ if (declared_arg_types[i] == ANYCOMPATIBLERANGEOID ||
+ declared_arg_types[i] == ANYCOMPATIBLEMULTIRANGEOID)
+ return NULL; /* OK */
+ }
+ return psprintf(_("A result of type %s requires at least one input of type anycompatiblerange or anycompatiblemultirange."),
+ format_type_be(ret_type));
}
else if (IsPolymorphicTypeFamily1(ret_type))
{
@@ -2442,7 +2717,7 @@ check_valid_polymorphic_signature(Oid ret_type,
return NULL; /* OK */
}
/* Keep this list in sync with IsPolymorphicTypeFamily1! */
- return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange."),
+ return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange."),
format_type_be(ret_type));
}
else if (IsPolymorphicTypeFamily2(ret_type))
@@ -2594,6 +2869,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
+ /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+ if (targettype == ANYMULTIRANGEOID || targettype == ANYCOMPATIBLEMULTIRANGEOID)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 5d2aca8cfe..1a08218de9 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.o \
name.o \
network.o \
network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644
index 0000000000..bd3cf3dc93
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,2186 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ FmgrInfo typioproc; /* range type's I/O proc */
+ Oid typioparam; /* range type's I/O parameter */
+} MultirangeIOData;
+
+typedef enum
+{
+ MULTIRANGE_BEFORE_RANGE,
+ MULTIRANGE_IN_RANGE,
+ MULTIRANGE_IN_RANGE_ESCAPED,
+ MULTIRANGE_IN_RANGE_QUOTED,
+ MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+ MULTIRANGE_AFTER_RANGE,
+ MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+static MultirangeIOData *get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid,
+ IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges);
+
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list,
+ * with zero or more ranges separated by commas.
+ * We accept whitespace anywhere:
+ * before/after our brackets and around the commas.
+ * Ranges can be the empty literal or some stuff inside parens/brackets.
+ * Mostly we delegate parsing the individual range contents
+ * to range_in, but we have to detect quoting and backslash-escaping
+ * which can happen for range bounds.
+ * Backslashes can escape something inside or outside a quoted string,
+ * and a quoted string can escape quote marks either either backslashes
+ * or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+ TypeCacheEntry *rangetyp;
+ int32 ranges_seen = 0;
+ int32 range_count = 0;
+ int32 range_capacity = 8;
+ RangeType *range;
+ RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+ MultirangeIOData *cache;
+ MultirangeType *ret;
+ MultirangeParseState parse_state;
+ const char *ptr = input_str;
+ const char *range_str = NULL;
+ int32 range_str_len;
+ char *range_str_copy;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+ rangetyp = cache->typcache->rngtype;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr == '{')
+ ptr++;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Missing left bracket.")));
+
+ /* consume ranges */
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Unexpected end of input.")));
+
+ /* skip whitespace */ ;
+ if (isspace((unsigned char) ch))
+ continue;
+
+ switch (parse_state)
+ {
+ case MULTIRANGE_BEFORE_RANGE:
+ if (ch == '[' || ch == '(')
+ {
+ range_str = ptr;
+ parse_state = MULTIRANGE_IN_RANGE;
+ }
+ else if (ch == '}' && ranges_seen == 0)
+ parse_state = MULTIRANGE_FINISHED;
+ else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ ranges_seen++;
+ /* nothing to do with an empty range */
+ ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected range start.")));
+ break;
+ case MULTIRANGE_IN_RANGE:
+ if (ch == '"')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+ else if (ch == ']' || ch == ')')
+ {
+ range_str_len = ptr - range_str + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, range_str, range_str_len);
+ if (range_capacity == range_count)
+ {
+ range_capacity *= 2;
+ ranges = (RangeType **)
+ repalloc(ranges, range_capacity * sizeof(RangeType *));
+ }
+ ranges_seen++;
+ range = DatumGetRangeTypeP(InputFunctionCall(&cache->typioproc,
+ range_str_copy,
+ cache->typioparam,
+ typmod));
+ if (!RangeIsEmpty(range))
+ ranges[range_count++] = range;
+ parse_state = MULTIRANGE_AFTER_RANGE;
+ }
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_IN_RANGE_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE;
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED:
+ if (ch == '"')
+ if (*(ptr + 1) == '"')
+ {
+ /* two quote marks means an escaped quote mark */
+ ptr++;
+ }
+ else
+ parse_state = MULTIRANGE_IN_RANGE;
+ else if (ch == '\\')
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+ else
+ /* include it in range_str */ ;
+ break;
+ case MULTIRANGE_AFTER_RANGE:
+ if (ch == ',')
+ parse_state = MULTIRANGE_BEFORE_RANGE;
+ else if (ch == '}')
+ parse_state = MULTIRANGE_FINISHED;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Expected comma or end of multirange.")));
+ break;
+ case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+ /* include it in range_str */
+ parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+ break;
+ default:
+ elog(ERROR, "unknown parse state: %d", parse_state);
+ }
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed multirange literal: \"%s\"",
+ input_str),
+ errdetail("Junk after right bracket.")));
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ MultirangeIOData *cache;
+ StringInfoData buf;
+ RangeType *range;
+ char *rangeStr;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->typioproc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid mltrngtypoid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ MultirangeIOData *cache;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(ReceiveFunctionCall(&cache->typioproc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+ PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+ MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(multirange);
+ StringInfo buf = makeStringInfo();
+ RangeType **ranges;
+ int32 range_count;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range;
+
+ range = RangeTypePGetDatum(ranges[i]);
+ range = PointerGetDatum(SendFunctionCall(&cache->typioproc, range));
+
+ pq_sendint32(buf, VARSIZE(range) - VARHDRSZ);
+ pq_sendbytes(buf, VARDATA(range), VARSIZE(range) - VARHDRSZ);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+ {
+ Oid typiofunc;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+ /* get_type_io_data does more than we need, but is convenient */
+ get_type_io_data(cache->typcache->rngtype->type_id,
+ func,
+ &typlen,
+ &typbyval,
+ &typalign,
+ &typdelim,
+ &cache->typioparam,
+ &typiofunc);
+
+ if (!OidIsValid(typiofunc))
+ {
+ /* this could only happen for receive or send */
+ if (func == IOFunc_receive)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary input function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("no binary output function available for type %s",
+ format_type_be(cache->typcache->rngtype->type_id))));
+ }
+ fmgr_info_cxt(typiofunc, &cache->typioproc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful for
+ * defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type. Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+ TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+ if (typcache == NULL ||
+ typcache->type_id != mltrngtypid)
+ {
+ typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+ fcinfo->flinfo->fn_extra = (void *) typcache;
+ }
+
+ return typcache;
+}
+
+/*
+ * This serializes the multirange from a list of non-null ranges.
+ * It also sorts the ranges and merges any that touch.
+ * The ranges should already be detoasted, and there should be no NULLs.
+ * This should be used by most callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+ RangeType **ranges)
+{
+ MultirangeType *multirange;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ bytelen += MAXALIGN(VARSIZE(ranges[i]));
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ memcpy(ptr, ranges[i], VARSIZE(ranges[i]));
+ ptr += MAXALIGN(VARSIZE(ranges[i]));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of arbitrary ranges into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ *
+ * Returns the number of slots actually used, which may be less than
+ * input_range_count but never more.
+ *
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+ RangeType **ranges)
+{
+ RangeType *lastRange = NULL;
+ RangeType *currentRange;
+ int32 i;
+ int32 output_range_count = 0;
+
+ /* Sort the ranges so we can find the ones that overlap/meet. */
+ qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+ rangetyp);
+
+ /* Now merge where possible: */
+ for (i = 0; i < input_range_count; i++)
+ {
+ currentRange = ranges[i];
+ if (RangeIsEmpty(currentRange))
+ continue;
+
+ if (lastRange == NULL)
+ {
+ ranges[output_range_count++] = lastRange = currentRange;
+ continue;
+ }
+
+ /*
+ * range_adjacent_internal gives true if *either* A meets B or B meets
+ * A, which is not quite want we want, but we rely on the sorting
+ * above to rule out B meets A ever happening.
+ */
+ if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+ {
+ /* The two ranges touch (without overlap), so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, false);
+ }
+ else if (range_before_internal(rangetyp, lastRange, currentRange))
+ {
+ /* There's a gap, so make a new entry: */
+ lastRange = ranges[output_range_count] = currentRange;
+ output_range_count++;
+ }
+ else
+ {
+ /* They must overlap, so merge them: */
+ ranges[output_range_count - 1] = lastRange =
+ range_union_internal(rangetyp, lastRange, currentRange, true);
+ }
+ }
+
+ return output_range_count;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(MultirangeType *multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.
+ * Since this is a variadic function we get passed an array.
+ * The array must contain range types that match our return value,
+ * and there must be no NULLs.
+ */
+Datum
+multirange_constructor2(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ ArrayType *rangeArray;
+ int range_count;
+ Datum *elements;
+ bool *nulls;
+ RangeType **ranges;
+ int dims;
+ int i;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * A no-arg invocation should call multirange_constructor0 instead, but
+ * returning an empty range is what that does.
+ */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_CARDINALITY_VIOLATION),
+ errmsg("multiranges cannot be constructed from multi-dimensional arrays")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
+
+ /*
+ * Be careful: we can still be called with zero ranges, like this:
+ * `int4multirange(variadic '{}'::int4range[])
+ */
+ if (dims == 0)
+ {
+ range_count = 0;
+ ranges = NULL;
+ }
+ else
+ {
+ deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+ rangetyp->typalign, &elements, &nulls, &range_count);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ if (nulls[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ /* make_multirange will do its own copy */
+ ranges[i] = DatumGetRangeTypeP(elements[i]);
+ }
+ }
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Construct multirange value from a single range.
+ * It'd be nice if we could just use multirange_constructor2
+ * for this case, but we need a non-variadic single-arg function
+ * to let us define a CAST from a range to its multirange.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ Oid rngtypid;
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ RangeType *range;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /*
+ * These checks should be guaranteed by our signature, but let's do them
+ * just in case.
+ */
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("multirange values cannot contain NULL members")));
+
+ range = PG_GETARG_RANGE_P(0);
+
+ /* Make sure the range type matches. */
+ rngtypid = RangeTypeGetOid(range);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %u does not match constructor type", rngtypid)));
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 1, &range));
+}
+
+/*
+ * Constructor just like multirange_constructor1,
+ * but opr_sanity gets angry if the same internal function
+ * handles multiple functions with different arg counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+ rangetyp = typcache->rngtype;
+
+ /* We should always be called with no arguments */
+
+ if (PG_NARGS() == 0)
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+ else
+ elog(ERROR, /* can't happen */
+ "niladic multirange constructor must not receive arguments");
+}
+
+
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+multirange_union(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ int32 range_count3;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType **ranges3;
+
+ if (MultirangeIsEmpty(mr1))
+ PG_RETURN_MULTIRANGE_P(mr2);
+ if (MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ range_count3 = range_count1 + range_count2;
+ ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+ memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+ memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+ PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+ range_count3, ranges3));
+}
+
+/* multirange minus */
+Datum
+multirange_minus(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(mr1);
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_minus_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_minus_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ /*
+ * Worst case: every range in ranges1 makes a different cut to some range
+ * in ranges2.
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep subtracting until it's gone or the ranges
+ * in mr2 have passed it. After a subtraction we assign what's left back
+ * to r1. The parallel progress through mr1 and mr2 is similar to
+ * multirange_overlaps_multirange_internal.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+ {
+ /*
+ * If r2 takes a bite out of the middle of r1, we need two
+ * outputs
+ */
+ range_count3++;
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /*
+ * If r2 overlaps r1, replace r1 with r1 - r2.
+ */
+ r1 = range_minus_internal(rangetyp, r1, r2);
+
+ /*
+ * If r2 goes past r1, then we need to stay with it, in case
+ * it hits future r1s. Otherwise we need to keep r1, in case
+ * future r2s hit it. Since we already subtracted, there's no
+ * point in using the overright/overleft calls.
+ */
+ if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+ break;
+ else
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ }
+ else
+ {
+ /*
+ * This and all future r2s are past r1, so keep them. Also
+ * assign whatever is left of r1 to the result.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Nothing else can remove anything from r1, so keep it. Even if r1 is
+ * empty here, make_multirange will remove it.
+ */
+ ranges3[range_count3++] = r1;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+multirange_intersect(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr1);
+ TypeCacheEntry *typcache;
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+ rangetyp = typcache->rngtype;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_MULTIRANGE_P(multirange_intersect_internal(mltrngtypoid,
+ rangetyp,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2));
+}
+
+MultirangeType *
+multirange_intersect_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+ int32 range_count1, RangeType **ranges1,
+ int32 range_count2, RangeType **ranges2)
+{
+ RangeType *r1;
+ RangeType *r2;
+ RangeType **ranges3;
+ int32 range_count3;
+ int32 i1;
+ int32 i2;
+
+ if (range_count1 == 0 || range_count2 == 0)
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+ /*-----------------------------------------------
+ * Worst case is a stitching pattern like this:
+ *
+ * mr1: --- --- --- ---
+ * mr2: --- --- ---
+ * mr3: - - - - - -
+ *
+ * That seems to be range_count1 + range_count2 - 1,
+ * but one extra won't hurt.
+ *-----------------------------------------------
+ */
+ ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+ range_count3 = 0;
+
+ /*
+ * For each range in mr1, keep intersecting until the ranges in mr2 have
+ * passed it. The parallel progress through mr1 and mr2 is similar to
+ * multirange_minus_multirange_internal, but we don't have to assign back
+ * to r1.
+ */
+ r2 = ranges2[0];
+ for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+ {
+ r1 = ranges1[i1];
+
+ /* Discard r2s while r2 << r1 */
+ while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+ {
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+ }
+
+ while (r2 != NULL)
+ {
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ {
+ /* Keep the overlapping part */
+ ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+ /* If we "used up" all of r2, go to the next one... */
+ if (range_overleft_internal(rangetyp, r2, r1))
+ r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+ /* ...otherwise go to the next r1 */
+ else
+ break;
+ }
+ else
+ /* We're past r1, so move to the next one */
+ break;
+ }
+
+ /* If we're out of r2s, there can be no more intersections */
+ if (r2 == NULL)
+ break;
+ }
+
+ return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ ArrayBuildState *state;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_agg must be called with a range")));
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(rngtypoid, aggContext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ /* skip NULLs */
+ if (!PG_ARGISNULL(1))
+ accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+ PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ ArrayBuildState *state;
+ int32 range_count;
+ RangeType **ranges;
+ int i;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (state == NULL)
+ /* This shouldn't be possible, but just in case.... */
+ PG_RETURN_NULL();
+
+ /* Also return NULL if we had zero inputs, like other aggregates */
+ range_count = state->nelems;
+ if (range_count == 0)
+ PG_RETURN_NULL();
+
+ mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+ PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid mltrngtypoid;
+ TypeCacheEntry *typcache;
+ MultirangeType *result;
+ MultirangeType *current;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+ mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_multirange(mltrngtypoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("range_intersect_agg must be called with a multirange")));
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_MULTIRANGE_P(0);
+ current = PG_GETARG_MULTIRANGE_P(1);
+
+ multirange_deserialize(result, &range_count1, &ranges1);
+ multirange_deserialize(current, &range_count2, &ranges2);
+
+ result = multirange_intersect_internal(mltrngtypoid,
+ typcache->rngtype,
+ range_count1,
+ ranges1,
+ range_count2,
+ ranges2);
+ PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum val = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ Datum val = PG_GETARG_DATUM(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ return false;
+ }
+
+ return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+ return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType *mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * Empties never overlap, even with empties. (This seems strange since
+ * they *do* contain each other, but we want to follow how ranges work.)
+ */
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+ * we can use their ordering to avoid O(n^2). This is similar to
+ * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+ * don't find an overlap with r we're done, and here if we don't find an
+ * overlap with r2 we try the next r2.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ return true;
+ }
+
+ /* We looked through all of mr2 without finding an overlap */
+ return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype,
+ ranges1[range_count1 - 1],
+ ranges2[range_count2 - 1]));
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype,
+ ranges1[0],
+ ranges2[0]));
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1, MultirangeType *mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * We follow the same logic for empties as ranges: - an empty multirange
+ * contains an empty range/multirange. - an empty multirange can't contain
+ * any other range/multirange. - an empty multirange is contained by any
+ * other range/multirange.
+ */
+
+ if (range_count2 == 0)
+ return true;
+ if (range_count1 == 0)
+ return false;
+
+ /*
+ * Every range in mr2 must be contained by some range in mr1. To avoid
+ * O(n^2) we walk through both ranges in tandem.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 @> r2, go to the next r2, otherwise return false (since every
+ * r1[n] and r1[n+1] must have a gap). Note this will give weird
+ * answers if you don't canonicalize, e.g. with a custom
+ * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+ * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+ * '[2,2]'::int2range is false.
+ */
+ if (!range_contains_internal(rangetyp, r1, r2))
+ return false;
+ }
+
+ /* All ranges in mr2 are satisfied */
+ return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ PG_RETURN_BOOL(range_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+ MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 range_count_max;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ TypeCacheEntry *typcache;
+ int cmp = 0; /* If both are empty we'll use this. */
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * If one multirange is shorter, it's as if it had empty ranges at the
+ * end to extend its length. An empty range compares earlier than any
+ * other range, so the shorter multirange comes before the longer.
+ * This is the same behavior as in other types, e.g. in strings 'aaa'
+ * < 'aaaaaa'.
+ */
+ if (i >= range_count_1)
+ {
+ cmp = -1;
+ break;
+ }
+ if (i >= range_count_2)
+ {
+ cmp = 1;
+ break;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ if (cmp != 0)
+ break;
+ }
+
+ PG_FREE_IF_COPY(mr1, 0);
+ PG_FREE_IF_COPY(mr2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = multirange_cmp(fcinfo);
+
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Oid mltrngtypoid = MultirangeTypeGetOid(mr);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ uint64 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 18f2ee8226..248ea7f44a 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -52,6 +52,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
}
Datum
+binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
+binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
+{
+ Oid typoid = PG_GETARG_OID(0);
+
+ CHECK_IS_BINARY_UPGRADE;
+ binary_upgrade_next_mrng_array_pg_type_oid = typoid;
+
+ PG_RETURN_VOID();
+}
+
+Datum
binary_upgrade_set_next_toast_pg_type_oid(PG_FUNCTION_ARGS)
{
Oid typoid = PG_GETARG_OID(0);
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 3d6b2f9093..99a93271fe 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -228,6 +229,43 @@ anycompatiblerange_out(PG_FUNCTION_ARGS)
}
/*
+ * anycompatiblemultirange
+ *
+ * We may as well allow output, since multirange_out will in fact work.
+ */
+PSEUDOTYPE_DUMMY_INPUT_FUNC(anycompatiblemultirange);
+
+Datum
+anycompatiblemultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
+/*
+ * anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type %s", "anymultirange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+ return multirange_out(fcinfo);
+}
+
+/*
* void
*
* We support void_in so that PL functions can return VOID without any
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 01ad8bc240..0af3c67296 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -43,8 +43,6 @@
#include "utils/timestamp.h"
-#define RANGE_EMPTY_LITERAL "empty"
-
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@@ -431,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -452,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -475,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -485,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -495,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -505,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -515,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -957,7 +986,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
+ RangeType *ret;
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ ret = range_minus_internal(typcache, r1, r2);
+ if (ret)
+ PG_RETURN_RANGE_P(ret);
+ else
+ PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -969,18 +1016,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
- PG_RETURN_RANGE_P(r1);
+ return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -993,34 +1034,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
- PG_RETURN_RANGE_P(r1);
+ return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
- PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+ return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
- PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+ return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
- PG_RETURN_NULL();
+ return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
-static RangeType *
+RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@@ -1101,6 +1142,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
+
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
@@ -1110,17 +1164,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
- /* Different types should be prevented by ANYRANGE matching rules */
- if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
- elog(ERROR, "range types do not match");
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
- if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
- PG_RETURN_RANGE_P(make_empty_range(typcache));
+ if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@@ -1132,9 +1180,81 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
- PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+ return make_range(typcache, result_lower, result_upper, false);
+}
+
+/* range, range -> range, range functions */
+
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+ RangeType **output1, RangeType **output2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+ range_cmp_bounds(typcache, &upper1, &upper2) > 0)
+ {
+ /*
+ * Need to invert inclusive/exclusive for the lower2 and upper2
+ * points. They can't be infinite though. We're allowed to overwrite
+ * these RangeBounds since they only exist locally.
+ */
+ lower2.inclusive = !lower2.inclusive;
+ lower2.lower = false;
+ upper2.inclusive = !upper2.inclusive;
+ upper2.lower = true;
+
+ *output1 = make_range(typcache, &lower1, &lower2, false);
+ *output2 = make_range(typcache, &upper2, &upper1, false);
+ return true;
+ }
+
+ return false;
}
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+ MemoryContext aggContext;
+ Oid rngtypoid;
+ TypeCacheEntry *typcache;
+ RangeType *result;
+ RangeType *current;
+
+ if (!AggCheckCallContext(fcinfo, &aggContext))
+ elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+ rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ if (!type_is_range(rngtypoid))
+ ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+ typcache = range_get_typcache(fcinfo, rngtypoid);
+
+ /* strictness ensures these are non-null */
+ result = PG_GETARG_RANGE_P(0);
+ current = PG_GETARG_RANGE_P(1);
+
+ result = range_intersect_internal(typcache, result, current);
+ PG_RETURN_RANGE_P(result);
+}
+
+
/* Btree support */
/* btree comparator */
@@ -1144,12 +1264,6 @@ range_cmp(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
- RangeBound lower1,
- lower2;
- RangeBound upper1,
- upper2;
- bool empty1,
- empty2;
int cmp;
check_stack_depth(); /* recurses when subtype is a range type */
@@ -1160,6 +1274,28 @@ range_cmp(PG_FUNCTION_ARGS)
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+ cmp = range_cmp_internal(typcache, r1, r2);
+
+ PG_FREE_IF_COPY(r1, 0);
+ PG_FREE_IF_COPY(r2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
@@ -1177,10 +1313,7 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
- PG_FREE_IF_COPY(r1, 0);
- PG_FREE_IF_COPY(r2, 1);
-
- PG_RETURN_INT32(cmp);
+ return cmp;
}
/* inequality operators using the range_cmp function */
@@ -1218,13 +1351,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1363,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1404,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1433,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1471,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1770,6 +1917,21 @@ range_get_flags(const RangeType *range)
}
/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
+/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
* This is only needed in GiST operations, so we don't include a provision
@@ -1938,6 +2100,46 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
}
/*
+ * qsort callback for sorting ranges.
+ *
+ * Two empty ranges compare equal; an empty range sorts to the left of any
+ * non-empty range. Two non-empty ranges are sorted by lower bound first
+ * and by upper bound next.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+ RangeType *r1 = *(RangeType **) key1;
+ RangeType *r2 = *(RangeType **) key2;
+ TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+ RangeBound lower1;
+ RangeBound upper1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty1;
+ bool empty2;
+ int cmp;
+
+ range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+ range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 && empty2)
+ cmp = 0;
+ else if (empty1)
+ cmp = -1;
+ else if (empty2)
+ cmp = 1;
+ else
+ {
+ cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+ if (cmp == 0)
+ cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+ }
+
+ return cmp;
+}
+
+/*
* Build an empty range value of the type indicated by the typcache entry.
*/
RangeType *
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index a7d63f107f..b137461de7 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2530,6 +2530,16 @@ type_is_range(Oid typid)
}
/*
+ * type_is_multirange
+ * Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
+/*
* get_type_category_preferred
*
* Given the type OID, fetch its category and preferred-type status.
@@ -3184,7 +3194,7 @@ get_namespace_name_or_temp(Oid nspid)
return get_namespace_name(nspid);
}
-/* ---------- PG_RANGE CACHE ---------- */
+/* ---------- PG_RANGE CACHES ---------- */
/*
* get_range_subtype
@@ -3237,6 +3247,56 @@ get_range_collation(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_range_multirange
+ * Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngmultitypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
+/*
+ * get_range_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_range_multirange_subtype(Oid multirangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGEMULTIRANGE, ObjectIdGetDatum(multirangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngtypid;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 53d9ddf159..7654331a59 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -651,6 +651,18 @@ static const struct cachedesc cacheinfo[] = {
},
64
},
+ {RangeRelationId, /* RANGEMULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngmultitypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+
{RangeRelationId, /* RANGETYPE */
RangeTypidIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 854f133f9b..160846cf10 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -286,6 +286,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -302,6 +303,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -553,8 +557,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
- * checking for ranges because ranges can't be created in the first
- * place unless their subtypes support equality.)
+ * checking for ranges and multiranges because ranges can't be created
+ * in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@@ -591,7 +595,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@@ -616,7 +620,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@@ -641,7 +645,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
- * we need no special check for ranges.
+ * we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@@ -693,6 +697,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange.
+ */
+ if (hash_proc == F_HASH_MULTIRANGE &&
+ !multirange_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -737,6 +748,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
+ /*
+ * Likewise for hash_multirange_extended.
+ */
+ if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+ !multirange_element_has_extended_hashing(typentry))
+ hash_extended_proc = InvalidOid;
+
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -817,6 +835,16 @@ lookup_type_cache(Oid type_id, int flags)
}
/*
+ * If requested, get information about a multirange type
+ */
+ if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+ typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ {
+ load_multirangetype_info(typentry);
+ }
+
+ /*
* If requested, get information about a domain type
*/
if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
@@ -927,6 +955,22 @@ load_rangetype_info(TypeCacheEntry *typentry)
typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
}
+/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+ Oid rangetypeOid;
+
+ rangetypeOid = get_range_multirange_subtype(typentry->type_id);
+ if (!OidIsValid(rangetypeOid))
+ elog(ERROR, "cache lookup failed for multirange type %u",
+ typentry->type_id);
+
+ typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
/*
* load_domaintype_info --- helper routine to set up domain constraint info
@@ -1527,11 +1571,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
*/
static bool
@@ -1574,6 +1618,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_multirange_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+ /* load up range link if we didn't already */
+ if (typentry->rngtype == NULL &&
+ typentry->typtype == TYPTYPE_MULTIRANGE)
+ load_multirangetype_info(typentry);
+
+ if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+ {
+ TypeCacheEntry *elementry;
+
+ /* might need to calculate subtype's hash function properties */
+ elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+ TYPECACHE_HASH_PROC |
+ TYPECACHE_HASH_EXTENDED_PROC);
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+ if (OidIsValid(elementry->hash_extended_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 78ed857203..8a5bd9bf19 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -35,6 +35,7 @@ typedef struct polymorphic_actuals
Oid anyelement_type; /* anyelement mapping, if known */
Oid anyarray_type; /* anyarray mapping, if known */
Oid anyrange_type; /* anyrange mapping, if known */
+ Oid anymultirange_type; /* anymultirange mapping, if known */
} polymorphic_actuals;
static void shutdown_MultiFuncCall(Datum arg);
@@ -46,6 +47,7 @@ static TypeFuncClass internal_get_result_type(Oid funcid,
static void resolve_anyelement_from_others(polymorphic_actuals *actuals);
static void resolve_anyarray_from_others(polymorphic_actuals *actuals);
static void resolve_anyrange_from_others(polymorphic_actuals *actuals);
+static void resolve_anymultirange_from_others(polymorphic_actuals *actuals);
static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
oidvector *declared_args,
Node *call_expr);
@@ -503,6 +505,31 @@ resolve_anyelement_from_others(polymorphic_actuals *actuals)
format_type_be(range_base_type))));
actuals->anyelement_type = range_typelem;
}
+ else if (OidIsValid(actuals->anymultirange_type))
+ {
+ /* Use the element type based on the multirange type */
+ Oid multirange_base_type = getBaseType(actuals->anymultirange_type);
+ Oid multirange_typelem =
+ get_range_multirange_subtype(multirange_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_base_type))));
+
+ Oid range_base_type = getBaseType(multirange_typelem);
+ Oid range_typelem = get_range_subtype(range_base_type);
+
+ if (!OidIsValid(range_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s does not contain a range type but type %s",
+ "anymultirange",
+ format_type_be(range_base_type))));
+ actuals->anyelement_type = range_typelem;
+ }
else
elog(ERROR, "could not determine polymorphic type");
}
@@ -540,10 +567,54 @@ static void
resolve_anyrange_from_others(polymorphic_actuals *actuals)
{
/*
- * We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types with the same subtype.
+ * We can't deduce a range type from other polymorphic array or base types,
+ * because there may be multiple range types with the same subtype, but we
+ * can deduce it from a polymorphic multirange type.
*/
- elog(ERROR, "could not determine polymorphic type");
+ if (OidIsValid(actuals->anymultirange_type))
+ {
+ /* Use the element type based on the multirange type */
+ Oid multirange_base_type = getBaseType(actuals->anymultirange_type);
+ Oid multirange_typelem =
+ get_range_multirange_subtype(multirange_base_type);
+
+ if (!OidIsValid(multirange_typelem))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange",
+ format_type_be(multirange_base_type))));
+ actuals->anyrange_type = multirange_typelem;
+ }
+ else
+ elog(ERROR, "could not determine polymorphic type");
+}
+
+/*
+ * Resolve actual type of ANYMULTIRANGE from other polymorphic inputs
+ */
+static void
+resolve_anymultirange_from_others(polymorphic_actuals *actuals)
+{
+ /*
+ * We can't deduce a multirange type from polymorphic array or base types,
+ * because there may be multiple range types with the same subtype, but we
+ * can deduce it from a polymorphic range type.
+ */
+ if (OidIsValid(actuals->anyrange_type))
+ {
+ Oid range_base_type = getBaseType(actuals->anyrange_type);
+ Oid multirange_typeid = get_range_multirange(range_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(actuals->anyrange_type))));
+ actuals->anymultirange_type = multirange_typeid;
+ }
+ else
+ elog(ERROR, "could not determine polymorphic type");
}
/*
@@ -566,9 +637,11 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anycompatible_result = false;
bool have_anycompatible_array_result = false;
bool have_anycompatible_range_result = false;
+ bool have_anycompatible_multirange_result = false;
polymorphic_actuals poly_actuals;
polymorphic_actuals anyc_actuals;
Oid anycollation = InvalidOid;
@@ -594,6 +667,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
have_polymorphic_result = true;
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_polymorphic_result = true;
+ have_anymultirange_result = true;
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
have_polymorphic_result = true;
@@ -607,6 +684,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
have_polymorphic_result = true;
have_anycompatible_range_result = true;
break;
+ case ANYCOMPATIBLEMULTIRANGEOID:
+ have_polymorphic_result = true;
+ have_anycompatible_multirange_result = true;
+ break;
default:
break;
}
@@ -660,6 +741,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
}
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ {
+ poly_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ return false;
+ }
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
if (!OidIsValid(anyc_actuals.anyelement_type))
@@ -688,6 +778,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
}
break;
+ case ANYCOMPATIBLEMULTIRANGEOID:
+ if (!OidIsValid(anyc_actuals.anymultirange_type))
+ {
+ anyc_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, i);
+ if (!OidIsValid(anyc_actuals.anymultirange_type))
+ return false;
+ }
+ break;
default:
break;
}
@@ -703,6 +802,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&poly_actuals);
+
if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
resolve_anyelement_from_others(&anyc_actuals);
@@ -712,6 +814,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
resolve_anyrange_from_others(&anyc_actuals);
+ if (have_anycompatible_multirange_result && !OidIsValid(anyc_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&anyc_actuals);
+
/*
* Identify the collation to use for polymorphic OUT parameters. (It'll
* necessarily be the same for both anyelement and anyarray, likewise for
@@ -780,6 +885,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ poly_actuals.anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
TupleDescInitEntry(tupdesc, i + 1,
@@ -805,6 +918,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
+ case ANYCOMPATIBLEMULTIRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(att->attname),
+ anyc_actuals.anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
default:
break;
}
@@ -834,9 +955,11 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
+ bool have_anymultirange_result = false;
bool have_anycompatible_result = false;
bool have_anycompatible_array_result = false;
bool have_anycompatible_range_result = false;
+ bool have_anycompatible_multirange_result = false;
polymorphic_actuals poly_actuals;
polymorphic_actuals anyc_actuals;
int inargno;
@@ -912,6 +1035,24 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = poly_actuals.anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
+ have_anymultirange_result = true;
+ }
+ else
+ {
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ {
+ poly_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(poly_actuals.anymultirange_type))
+ return false;
+ }
+ argtypes[i] = poly_actuals.anymultirange_type;
+ }
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
@@ -967,6 +1108,24 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyc_actuals.anyrange_type;
}
break;
+ case ANYCOMPATIBLEMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ {
+ have_polymorphic_result = true;
+ have_anycompatible_multirange_result = true;
+ }
+ else
+ {
+ if (!OidIsValid(anyc_actuals.anymultirange_type))
+ {
+ anyc_actuals.anymultirange_type =
+ get_call_expr_argtype(call_expr, inargno);
+ if (!OidIsValid(anyc_actuals.anymultirange_type))
+ return false;
+ }
+ argtypes[i] = anyc_actuals.anymultirange_type;
+ }
+ break;
default:
break;
}
@@ -988,6 +1147,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
+ if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&poly_actuals);
+
if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
resolve_anyelement_from_others(&anyc_actuals);
@@ -997,6 +1159,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
resolve_anyrange_from_others(&anyc_actuals);
+ if (have_anycompatible_multirange_result && !OidIsValid(anyc_actuals.anymultirange_type))
+ resolve_anymultirange_from_others(&anyc_actuals);
+
/* And finally replace the output column types as needed */
for (i = 0; i < numargs; i++)
{
@@ -1013,6 +1178,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = poly_actuals.anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = poly_actuals.anymultirange_type;
+ break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
argtypes[i] = anyc_actuals.anyelement_type;
@@ -1023,6 +1191,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYCOMPATIBLERANGEOID:
argtypes[i] = anyc_actuals.anyrange_type;
break;
+ case ANYCOMPATIBLEMULTIRANGEOID:
+ argtypes[i] = anyc_actuals.anymultirange_type;
+ break;
default:
break;
}
@@ -1052,6 +1223,7 @@ get_type_func_class(Oid typid, Oid *base_typeid)
case TYPTYPE_BASE:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
+ case TYPTYPE_MULTIRANGE:
return TYPEFUNC_SCALAR;
case TYPTYPE_DOMAIN:
*base_typeid = typid = getBaseType(typid);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index c579227b19..5d09aa3d1e 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -276,7 +276,8 @@ static void dumpSearchPath(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type);
+ bool force_array_type,
+ bool include_multirange_type);
static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
static void binary_upgrade_set_pg_class_oids(Archive *fout,
@@ -1648,7 +1649,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
- if (tyinfo->isArray)
+ if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@@ -4432,16 +4433,49 @@ append_depends_on_extension(Archive *fout,
}
}
+static Oid
+get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+{
+ /*
+ * If the old version didn't assign an array type, but the new version
+ * does, we must select an unused type OID to assign. This currently only
+ * happens for domains, when upgrading pre-v11 to v11 and up.
+ *
+ * Note: local state here is kind of ugly, but we must have some, since we
+ * mustn't choose the same unused OID more than once.
+ */
+ static Oid next_possible_free_oid = FirstNormalObjectId;
+ PGresult *res;
+ bool is_dup;
+
+ do
+ {
+ ++next_possible_free_oid;
+ printfPQExpBuffer(upgrade_query,
+ "SELECT EXISTS(SELECT 1 "
+ "FROM pg_catalog.pg_type "
+ "WHERE oid = '%u'::pg_catalog.oid);",
+ next_possible_free_oid);
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+ is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
+ PQclear(res);
+ } while (is_dup);
+
+ return next_possible_free_oid;
+}
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
- bool force_array_type)
+ bool force_array_type,
+ bool include_multirange_type)
{
PQExpBuffer upgrade_query = createPQExpBuffer();
PGresult *res;
Oid pg_type_array_oid;
+ Oid pg_type_multirange_oid;
+ Oid pg_type_multirange_array_oid;
appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
@@ -4462,33 +4496,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQclear(res);
if (!OidIsValid(pg_type_array_oid) && force_array_type)
- {
- /*
- * If the old version didn't assign an array type, but the new version
- * does, we must select an unused type OID to assign. This currently
- * only happens for domains, when upgrading pre-v11 to v11 and up.
- *
- * Note: local state here is kind of ugly, but we must have some,
- * since we mustn't choose the same unused OID more than once.
- */
- static Oid next_possible_free_oid = FirstNormalObjectId;
- bool is_dup;
-
- do
- {
- ++next_possible_free_oid;
- printfPQExpBuffer(upgrade_query,
- "SELECT EXISTS(SELECT 1 "
- "FROM pg_catalog.pg_type "
- "WHERE oid = '%u'::pg_catalog.oid);",
- next_possible_free_oid);
- res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
- is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
- PQclear(res);
- } while (is_dup);
-
- pg_type_array_oid = next_possible_free_oid;
- }
+ pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
if (OidIsValid(pg_type_array_oid))
{
@@ -4499,6 +4507,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
pg_type_array_oid);
}
+ /*
+ * Pre-set the multirange type oid and its own array type oid.
+ */
+ if (include_multirange_type)
+ {
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBuffer(upgrade_query,
+ "SELECT t.oid, t.typarray "
+ "FROM pg_catalog.pg_type t "
+ "JOIN pg_catalog.pg_range r "
+ "ON t.oid = r.rngmultitypid "
+ "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
+ pg_type_oid);
+
+ res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+
+ pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+ pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
+
+ PQclear(res);
+ }
+ else
+ {
+ pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+ }
+
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_oid);
+ appendPQExpBufferStr(upgrade_buffer,
+ "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
+ appendPQExpBuffer(upgrade_buffer,
+ "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+ pg_type_multirange_array_oid);
+ }
+
destroyPQExpBuffer(upgrade_query);
}
@@ -4532,7 +4580,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
- pg_type_oid, false);
+ pg_type_oid, false, false);
if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
{
@@ -5081,6 +5129,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
+ if (tyinfo[i].typtype == 'm')
+ tyinfo[i].isMultirange = true;
+ else
+ tyinfo[i].isMultirange = false;
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
@@ -8219,9 +8272,12 @@ getProcLangs(Archive *fout, int *numProcLangs)
/*
* getCasts
- * get basic information about every cast in the system
+ * get basic information about most casts in the system
*
* numCasts is set to the number of casts read in
+ *
+ * Skip casts from a range to its multirange, since we'll create those
+ * automatically.
*/
CastInfo *
getCasts(Archive *fout, int *numCasts)
@@ -8239,7 +8295,20 @@ getCasts(Archive *fout, int *numCasts)
int i_castcontext;
int i_castmethod;
- if (fout->remoteVersion >= 80400)
+ if (fout->remoteVersion >= 130000)
+ {
+ appendPQExpBufferStr(query, "SELECT tableoid, oid, "
+ "castsource, casttarget, castfunc, castcontext, "
+ "castmethod "
+ "FROM pg_cast c "
+ "WHERE NOT EXISTS ( "
+ "SELECT 1 FROM pg_range r "
+ "WHERE c.castsource = r.rngtypid "
+ "AND c.casttarget = r.rngmultitypid "
+ ") "
+ "ORDER BY 3,4");
+ }
+ else if (fout->remoteVersion >= 80400)
{
appendPQExpBufferStr(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
@@ -10427,7 +10496,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
qualtypname);
@@ -10553,7 +10622,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, true);
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
qualtypname);
@@ -10659,7 +10728,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
qualtypname);
@@ -10864,7 +10933,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
@@ -11051,7 +11120,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- true); /* force array type */
+ true, /* force array type */
+ false); /* force multirange type */
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -11239,7 +11309,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
{
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
- false);
+ false, false);
binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
}
@@ -11513,7 +11583,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
stinfo->baseType->dobj.catId.oid,
- false);
+ false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
fmtQualifiedDumpable(stinfo));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 61c909e06d..36178db881 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -176,6 +176,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 70157cf90a..c262265b95 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -139,8 +139,8 @@
* * we need to estimate alignment padding cost abstractly, ie without
* reference to a real tuple. We must assume the worst case that
* all varlenas are aligned.
- * * within arrays, we unconditionally align varlenas (XXX this should be
- * revisited, probably).
+ * * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ * should be revisited, probably).
*
* The attalign cases are tested in what is hopefully something like their
* frequency of occurrence.
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 12d94fe1b3..01d95117cd 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -16,6 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 8be303870f..566f9364c5 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -327,6 +327,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_range_rngmultitypid_index, 8001, on pg_range using btree(rngmultitypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index ffabe275c0..03bab74253 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
aggtransfn => 'bytea_string_agg_transfn',
aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+ aggtransfn => 'range_intersect_agg_transfn',
+ aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+ aggtransfn => 'multirange_intersect_agg_transfn',
+ aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+ aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+ aggtranstype => 'internal' },
+
# json
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index b5dfaad9ec..604286e2c2 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1378,6 +1378,28 @@
amoprighttype => 'anyrange', amopstrategy => '18',
amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '2',
+ amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '3',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '4',
+ amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '5',
+ amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+ amoprighttype => 'anymultirange', amopstrategy => '1',
+ amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
# SP-GiST quad_point_ops
{ amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
amoprighttype => 'point', amopstrategy => '11', amopopr => '>^(point,point)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 37b580883f..9842e80acf 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -285,6 +285,8 @@
amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
{ amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
{ amprocfamily => 'btree/xid8_ops', amproclefttype => 'xid8',
@@ -453,6 +455,11 @@
{ amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '2',
amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+ amprocrighttype => 'anymultirange', amprocnum => '2',
+ amproc => 'hash_multirange_extended' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
{ amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
@@ -1274,12 +1281,13 @@
amprocrighttype => 'anyrange', amprocnum => '4',
amproc => 'brin_inclusion_union' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+ amprocrighttype => 'anyrange', amprocnum => '11',
+ amproc => 'range_merge(anyrange,anyrange)' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '13',
amproc => 'range_contains' },
{ amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
- amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+ amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
# minmax pg_lsn
{ amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index 5a58f50fbb..d6ca624add 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -530,4 +530,17 @@
{ castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
castcontext => 'e', castmethod => 'f' },
+# range to multirange
+{ castsource => 'int4range', casttarget => 'int4multirange', castfunc => 'int4multirange(int4range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'int8range', casttarget => 'int8multirange', castfunc => 'int8multirange(int8range)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'numrange', casttarget => 'nummultirange', castfunc => 'nummultirange(numrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'daterange', casttarget => 'datemultirange', castfunc => 'datemultirange(daterange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tsrange', casttarget => 'tsmultirange', castfunc => 'tsmultirange(tsrange)',
+ castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tstzrange', casttarget => 'tstzmultirange', castfunc => 'tstzmultirange(tstzrange)',
+ castcontext => 'e', castmethod => 'f' },
]
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index f2342bb328..9b0609defa 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -230,6 +230,10 @@
opcintype => 'anyrange' },
{ opcmethod => 'spgist', opcname => 'range_ops',
opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+ opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+ opcintype => 'anymultirange' },
{ opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
opcintype => 'box' },
{ opcmethod => 'spgist', opcname => 'quad_point_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 00ada7e48f..071352539f 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3327,5 +3327,174 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'matchingsel', oprjoin => 'matchingjoinsel' },
+{ oid => '8027', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+ oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+ oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+ oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+ oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+ oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+ descr => 'less than or equal',
+ oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+ oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+ descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+ oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+ descr => 'greater than',
+ oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+ oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+ oprcode => 'elem_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+ oprcode => 'range_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+ descr => 'is contained by',
+ oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+ oprcode => 'multirange_contained_by_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+ descr => 'overlaps or is left of',
+ oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+ descr => 'overlaps or is right of',
+ oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+ oprcode => 'range_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+ oprcode => 'multirange_adjacent_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+ oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+ oprcode => 'multirange_adjacent_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 4004138d77..dfc13fd6e5 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -230,5 +230,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 4bce3ad8de..5eb505fdda 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7208,6 +7208,14 @@
proname => 'anycompatiblerange_out', provolatile => 's',
prorettype => 'cstring', proargtypes => 'anycompatiblerange',
prosrc => 'anycompatiblerange_out' },
+{ oid => '8154', descr => 'I/O',
+ proname => 'anycompatiblemultirange_in', provolatile => 's',
+ prorettype => 'anycompatiblemultirange', proargtypes => 'cstring oid int4',
+ prosrc => 'anycompatiblemultirange_in' },
+{ oid => '8155', descr => 'I/O',
+ proname => 'anycompatiblemultirange_out', provolatile => 's',
+ prorettype => 'cstring', proargtypes => 'anycompatiblemultirange',
+ prosrc => 'anycompatiblemultirange_out' },
# tablesample method handlers
{ oid => '3313', descr => 'BERNOULLI tablesample method handler',
@@ -9797,6 +9805,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ descr => 'the smallest range which includes the whole multirange',
+ proname => 'range_merge', prorettype => 'anyrange',
+ proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@@ -9846,6 +9858,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anyrange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@@ -9914,6 +9933,258 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8114',
+ proname => 'multirange_union', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union' },
+{ oid => '8120',
+ proname => 'multirange_minus', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus' },
+{ oid => '8126',
+ proname => 'multirange_intersect', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8146', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 't',
+ prorettype => 'int4multirange', proargtypes => 'int4range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8147', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 't',
+ prorettype => 'nummultirange', proargtypes => 'numrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8148', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 't',
+ prorettype => 'tsmultirange', proargtypes => 'tsrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8149', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 't',
+ prorettype => 'tstzmultirange', proargtypes => 'tstzrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8150', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 't',
+ prorettype => 'datemultirange', proargtypes => 'daterange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8151', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 't',
+ prorettype => 'int8multirange', proargtypes => 'int8range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8152', descr => 'anymultirange cast',
+ proname => 'multirange', proisstrict => 't',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anymultirange',
+ prosrc => 'aggregate_dummy'},
+
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
@@ -10296,6 +10567,14 @@
proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
+{ oid => '8144', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
+{ oid => '8145', descr => 'for use by pg_upgrade',
+ proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
+ proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+ prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
{ oid => '3585', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index 479754c245..10060255c9 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
- rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+ rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+ rngmultitypid => 'int4multirange' },
{ rngtypid => 'numrange', rngsubtype => 'numeric',
rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
- rngsubdiff => 'numrange_subdiff' },
+ rngsubdiff => 'numrange_subdiff', rngmultitypid => 'nummultirange' },
{ rngtypid => 'tsrange', rngsubtype => 'timestamp',
rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
- rngsubdiff => 'tsrange_subdiff' },
+ rngsubdiff => 'tsrange_subdiff', rngmultitypid => 'tsmultirange' },
{ rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
- rngsubdiff => 'tstzrange_subdiff' },
+ rngsubdiff => 'tstzrange_subdiff', rngmultitypid => 'tstzmultirange' },
{ rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
- rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+ rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+ rngmultitypid => 'datemultirange' },
{ rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
- rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+ rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+ rngmultitypid => 'int8multirange' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index 98172bb1b6..60a8fde518 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -34,6 +34,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* OID of range's element type (subtype) */
Oid rngsubtype BKI_LOOKUP(pg_type);
+ /* OID of the range's multirange type */
+ Oid rngmultitypid BKI_LOOKUP(pg_type);
+
/* collation for this range type, or 0 */
Oid rngcollation BKI_DEFAULT(0);
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
- RegProcedure rangeSubDiff);
+ RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 78f44af5e7..741090cfe5 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -500,6 +500,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', array_type_oid => '8010', descr => 'multirange of integers',
+ typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8011', array_type_oid => '8012', descr => 'multirange of numerics',
+ typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8013', array_type_oid => '8014',
+ descr => 'multirange of timestamps without time zone',
+ typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8015', array_type_oid => '8016',
+ descr => 'multirange of timestamps with time zone',
+ typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '8017', array_type_oid => '8018', descr => 'multirange of dates',
+ typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '8019', array_type_oid => '8020', descr => 'multirange of bigints',
+ typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+ typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+ typreceive => 'multirange_recv', typsend => 'multirange_send',
+ typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@@ -629,5 +663,16 @@
typtype => 'p', typcategory => 'P', typinput => 'anycompatiblerange_in',
typoutput => 'anycompatiblerange_out', typreceive => '-', typsend => '-',
typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+ typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+ typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+ typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '8153',
+ descr => 'pseudo-type representing a multirange over a polymorphic common type',
+ typname => 'anycompatiblemultirange', typlen => '-1', typbyval => 'f',
+ typtype => 'p', typcategory => 'P', typinput => 'anycompatiblemultirange_in',
+ typoutput => 'anycompatiblemultirange_out', typreceive => '-', typsend => '-',
+ typalign => 'd', typstorage => 'x' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 7b37562648..63e7c940a2 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -263,6 +263,7 @@ typedef FormData_pg_type *Form_pg_type;
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
+#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@@ -304,13 +305,15 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#define IsPolymorphicTypeFamily2(typid) \
((typid) == ANYCOMPATIBLEOID || \
(typid) == ANYCOMPATIBLEARRAYOID || \
(typid) == ANYCOMPATIBLENONARRAYOID || \
- (typid) == ANYCOMPATIBLERANGEOID)
+ (typid) == ANYCOMPATIBLERANGEOID || \
+ (typid) == ANYCOMPATIBLEMULTIRANGEOID)
#endif /* EXPOSE_TO_CLIENT_CODE */
@@ -369,4 +372,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 23130895af..989914dfe1 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
+extern Oid AssignTypeMultirangeOid(void);
+extern Oid AssignTypeMultirangeArrayOid(void);
extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index c9c68e2f4f..d940d367f1 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -156,6 +156,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@@ -182,6 +183,8 @@ extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
extern Oid get_range_collation(Oid rangeOid);
+extern Oid get_range_multirange(Oid rangeOid);
+extern Oid get_range_multirange_subtype(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
extern bool get_index_isreplident(Oid index_oid);
extern bool get_index_isvalid(Oid index_oid);
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644
index 0000000000..469444776c
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ * Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. Note that ranges
+ * are varlena too, depending on whether they have lower/upper bounds and
+ * because even their base types can be varlena. So we can't really index
+ * into this list.
+ */
+} MultirangeType;
+
+/* Use this macro in preference to fetching multirangetypid field directly */
+#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+ Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+ RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType *mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+ MultirangeType *mr1,
+ MultirangeType *mr2);
+extern MultirangeType *multirange_minus_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+extern MultirangeType *multirange_intersect_internal(Oid mltrngtypoid,
+ TypeCacheEntry *rangetyp,
+ int32 range_count1,
+ RangeType **ranges1,
+ int32 range_count2,
+ RangeType **ranges2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+ Oid mltrngtypid);
+extern void multirange_deserialize(MultirangeType *range,
+ int32 *range_count, RangeType ***ranges);
+extern MultirangeType *make_multirange(Oid mltrngtypoid,
+ TypeCacheEntry *typcache, int32 range_count, RangeType **ranges);
+extern MultirangeType *make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 0cbbf09033..204aee4054 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
+#define RANGE_EMPTY_LITERAL "empty"
+
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
@@ -92,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -115,6 +125,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+ RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -125,6 +141,7 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
@@ -132,8 +149,12 @@ extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
-extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2, RangeType **output1,
+ RangeType **output2);
#endif /* RANGETYPES_H */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f27b73d76d..e8f393520a 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -79,6 +79,7 @@ enum SysCacheIdentifier
PUBLICATIONOID,
PUBLICATIONREL,
PUBLICATIONRELMAP,
+ RANGEMULTIRANGE,
RANGETYPE,
RELNAMENSP,
RELOID,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index cdd20e56d7..33f332a141 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -101,6 +101,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
/*
+ * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+ */
+ struct TypeCacheEntry *rngtype; /* multirange's range underlying type */
+
+ /*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
*/
@@ -143,6 +148,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 828ff5a288..1e3ff02289 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -513,6 +513,8 @@ do_compile(FunctionCallInfo fcinfo,
else if (rettypeid == ANYRANGEOID ||
rettypeid == ANYCOMPATIBLERANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY or ANYCOMPATIBLE */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2089,6 +2091,7 @@ build_datatype(HeapTuple typeTup, int32 typmod,
case TYPTYPE_BASE:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
+ case TYPTYPE_MULTIRANGE:
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_COMPOSITE:
@@ -2507,6 +2510,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYCOMPATIBLERANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..8232795148 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -140,6 +140,7 @@ owner of sequence deptest_a_seq
owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
+owner of type deptest_multirange
owner of type deptest_range
owner of table deptest2
owner of sequence ss1
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1
+-------+----------+-----------+-----------
+(0 rows)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..f8ca946d6a
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,2436 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR: malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+ ^
+DETAIL: Missing left bracket.
+select '{,}'::textmultirange;
+ERROR: malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR: malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR: malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR: malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+ ^
+DETAIL: Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR: malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR: malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+ ^
+DETAIL: Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR: malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+ ^
+DETAIL: Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR: malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR: malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+ ^
+DETAIL: Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR: malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+ ^
+DETAIL: Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR: range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+ ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {} '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty } '::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+ textmultirange
+----------------------------
+ {(" a a "," z z ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+ textmultirange
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange
+----------------
+ {[a,d)}
+(1 row)
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+ int4multirange
+----------------
+ {}
+(1 row)
+
+select int4range(1, 3)::int4multirange;
+ int4range
+-----------
+ {[1,3)}
+(1 row)
+
+select int4range(1, null)::int4multirange;
+ int4range
+-----------
+ {[1,)}
+(1 row)
+
+select int4range(null, null)::int4multirange;
+ int4range
+-----------
+ {(,)}
+(1 row)
+
+select 'empty'::textrange::textmultirange;
+ textmultirange
+----------------
+ {}
+(1 row)
+
+select textrange('a', 'c')::textmultirange;
+ textrange
+-----------
+ {[a,c)}
+(1 row)
+
+select textrange('a', null)::textmultirange;
+ textrange
+-----------
+ {[a,)}
+(1 row)
+
+select textrange(null, null)::textmultirange;
+ textrange
+-----------
+ {(,)}
+(1 row)
+
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | isempty | lower | upper
+-----------------------+---------+-------+-------
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {} | t | |
+ {(,5)} | f | | 5
+ {(,)} | f | |
+ {(,)} | f | |
+ {[1.1,2.2)} | f | 1.1 | 2.2
+ {[1.7,1.7],[1.9,2.1)} | f | 1.7 | 2.1
+ {[1.7,1.9)} | f | 1.7 | 1.9
+ {[3,)} | f | 3 |
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+ nmr | lower_inc | lower_inf | upper_inc | upper_inf
+-----------------------+-----------+-----------+-----------+-----------
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {} | f | f | f | f
+ {(,5)} | f | t | f | f
+ {(,)} | f | t | f | t
+ {(,)} | f | t | f | t
+ {[1.1,2.2)} | t | f | f | f
+ {[1.7,1.7],[1.9,2.1)} | t | f | f | f
+ {[1.7,1.9)} | t | f | f | f
+ {[3,)} | t | f | f | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+ nmr
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+ nmr
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+ nmr
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+ nmr
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+ nmr
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+ nmr
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+ nmr
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR: range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+ nummultirange
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+ nmr
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column?
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column?
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column?
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column?
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column?
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column?
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column?
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column?
+----------
+ f
+(1 row)
+
+-- union
+SELECT nummultirange() + nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+ ?column?
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT nummultirange() - nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+ ?column?
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+ ?column?
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+ ?column?
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+ ?column?
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() * nummultirange(numrange(1,2));
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+ ?column?
+----------
+ {}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+ ?column?
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+ ?column?
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+ ?column?
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+ ?column?
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column?
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+ ?column?
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id | range_agg
+---------+---------------------------------------------------
+ 1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+ 2 | {[07-01-2018,07-03-2018)}
+ 3 |
+ 4 |
+ 5 | {[07-01-2018,07-03-2018)}
+ 6 | {[07-01-2018,07-10-2018)}
+ 7 | {[07-01-2018,07-14-2018)}
+ 8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+ range_agg
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+ nmr
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+ nmr
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column?
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+ f8mr | i
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column?
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE: drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column?
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR: value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+ ?column?
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func
+-----------------------------
+ 11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR: function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds
+------------------
+ 18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds
+------------------
+ 124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql
+---------------------
+ 12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+ERROR: function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+create function anycompatiblearray_anycompatiblemultirange_func(a anycompatiblearray, mr anycompatiblemultirange)
+ returns anycompatible as 'select $1[1] + lower($2);' language sql;
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1,2], multirange(int4range(10,20)));
+ anycompatiblearray_anycompatiblemultirange_func
+-------------------------------------------------
+ 11
+(1 row)
+
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1,2], multirange(numrange(10,20)));
+ anycompatiblearray_anycompatiblemultirange_func
+-------------------------------------------------
+ 11
+(1 row)
+
+-- should fail
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1.1,2], multirange(int4range(10,20)));
+ERROR: function anycompatiblearray_anycompatiblemultirange_func(numeric[], int4multirange) does not exist
+LINE 1: select anycompatiblearray_anycompatiblemultirange_func(ARRAY...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anycompatiblearray_anycompatiblemultirange_func(anycompatiblearray, anycompatiblemultirange);
+create function anycompatiblerange_anycompatiblemultirange_func(r anycompatiblerange, mr anycompatiblemultirange)
+ returns anycompatible as 'select lower($1) + lower($2);' language sql;
+select anycompatiblerange_anycompatiblemultirange_func(int4range(1,2), multirange(int4range(10,20)));
+ anycompatiblerange_anycompatiblemultirange_func
+-------------------------------------------------
+ 11
+(1 row)
+
+-- should fail
+select anycompatiblerange_anycompatiblemultirange_func(numrange(1,2), multirange(int4range(10,20)));
+ERROR: function anycompatiblerange_anycompatiblemultirange_func(numrange, int4multirange) does not exist
+LINE 1: select anycompatiblerange_anycompatiblemultirange_func(numra...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anycompatiblerange_anycompatiblemultirange_func(anycompatiblerange, anycompatiblemultirange);
+-- should fail
+create function bogus_func(anycompatible)
+ returns anycompatiblerange as 'select int4range(1,10)' language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+ array
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 | f2
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+ arraymultirange
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+ERROR: range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column?
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+ t | u
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE: drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off; -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+ r | t
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+ r | t
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i | r
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+ i | r
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+ mr_polymorphic
+----------------
+ {[1,4)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anymultirange requires at least one input of type anyrange or anymultirange.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 8d350ab61b..044ce8835e 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -181,7 +185,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@@ -208,6 +215,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -228,6 +237,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@@ -340,7 +351,8 @@ WHERE p1.prorettype IN
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+----------------
@@ -355,20 +367,25 @@ ORDER BY 2;
3532 | enum_recv
(9 rows)
--- anyrange is tighter than the rest, can only resolve from anyrange input
+-- anyrange and anymultirange are tighter than the rest, can only resolve
+-- from each other
SELECT p1.oid, p1.proname
FROM pg_proc as p1
-WHERE p1.prorettype = 'anyrange'::regtype
+WHERE p1.prorettype IN ('anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
- 'anyrange'::regtype = ANY (p1.proargtypes)
+ ('anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
+ 8002 | anymultirange_in
3832 | anyrange_in
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(4 rows)
+(7 rows)
-- similarly for the anycompatible family
SELECT p1.oid, p1.proname
@@ -2193,13 +2210,14 @@ ORDER BY 1, 2, 3;
| float_ops | float4_ops | real
| float_ops | float8_ops | double precision
| jsonb_ops | jsonb_ops | jsonb
+ | multirange_ops | multirange_ops | anymultirange
| numeric_ops | numeric_ops | numeric
| range_ops | range_ops | anyrange
| record_image_ops | record_image_ops | record
| record_ops | record_ops | record
| tsquery_ops | tsquery_ops | tsquery
| tsvector_ops | tsvector_ops | tsvector
-(14 rows)
+(15 rows)
-- **************** pg_index ****************
-- Look for illegal values in pg_index fields.
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index d0a6b630b8..d55006d8c9 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -1811,7 +1811,7 @@ begin
return array[x + 1, x + 2];
end$$ language plpgsql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function f1(x anyrange) returns anyarray as $$
begin
return array[lower(x), upper(x)];
@@ -1856,7 +1856,7 @@ begin
return array[x + 1, x + 2];
end$$ language plpgsql;
ERROR: cannot determine result data type
-DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
create function f1(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
begin
return x;
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index 1ff40764d9..248c869931 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -61,7 +61,7 @@ create function polyf(x anyelement) returns anyrange as $$
select array[x + 1, x + 2]
$$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function polyf(x anyrange) returns anyarray as $$
select array[lower(x), upper(x)]
$$ language sql;
@@ -97,12 +97,27 @@ LINE 1: select polyf(int4range(42, 49), 11, 4.5) as fail;
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
drop function polyf(x anycompatiblerange, y anycompatible, z anycompatible);
+create function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
+ select array[lower(x), upper(x), y, z]
+$$ language sql;
+select polyf(multirange(int4range(42, 49)), 11, 2::smallint) as int, polyf(multirange(float8range(4.5, 7.8)), 7.8, 11::real) as num;
+ int | num
+--------------+------------------
+ {42,49,11,2} | {4.5,7.8,7.8,11}
+(1 row)
+
+select polyf(multirange(int4range(42, 49)), 11, 4.5) as fail; -- range type doesn't fit
+ERROR: function polyf(int4multirange, integer, numeric) does not exist
+LINE 1: select polyf(multirange(int4range(42, 49)), 11, 4.5) as fail...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible);
-- fail, can't infer type:
create function polyf(x anycompatible) returns anycompatiblerange as $$
select array[x + 1, x + 2]
$$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
create function polyf(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
select x
$$ language sql;
@@ -113,6 +128,22 @@ select polyf(int4range(42, 49), array[11]) as int, polyf(float8range(4.5, 7.8),
(1 row)
drop function polyf(x anycompatiblerange, y anycompatiblearray);
+-- fail, can't infer type:
+create function polyf(x anycompatible) returns anycompatiblemultirange as $$
+ select array[x + 1, x + 2]
+$$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anycompatiblemultirange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
+create function polyf(x anycompatiblemultirange, y anycompatiblearray) returns anycompatiblemultirange as $$
+ select x
+$$ language sql;
+select polyf(multirange(int4range(42, 49)), array[11]) as int, polyf(multirange(float8range(4.5, 7.8)), array[7]) as num;
+ int | num
+-----------+-------------
+ {[42,49)} | {[4.5,7.8)}
+(1 row)
+
+drop function polyf(x anycompatiblemultirange, y anycompatiblearray);
create function polyf(a anyelement, b anyarray,
c anycompatible, d anycompatible,
OUT x anyarray, OUT y anycompatiblearray)
@@ -231,7 +262,7 @@ CREATE AGGREGATE myaggp01a(*) (SFUNC = stfnp, STYPE = int4[],
CREATE AGGREGATE myaggp02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- N P
-- should CREATE
CREATE AGGREGATE myaggp03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -243,11 +274,11 @@ CREATE AGGREGATE myaggp03b(*) (SFUNC = stfp, STYPE = int4[],
CREATE AGGREGATE myaggp04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp04b(*) (SFUNC = stfp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- Case2 (R = P) && ((B = P) || (B = N))
-- -------------------------------------
-- S tf1 B tf2
@@ -302,13 +333,13 @@ ERROR: function tfp(integer[], anyelement) does not exist
CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N N P
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N P N
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -324,21 +355,21 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P N P
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P P N
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p,
@@ -368,11 +399,11 @@ CREATE AGGREGATE myaggn01b(*) (SFUNC = stfnp, STYPE = int4[],
CREATE AGGREGATE myaggn02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn02b(*) (SFUNC = stfnp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- N P
-- should CREATE
CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -382,7 +413,7 @@ CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
CREATE AGGREGATE myaggn04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- Case4 (R = N) && ((B = P) || (B = N))
-- -------------------------------------
-- S tf1 B tf2
@@ -436,21 +467,21 @@ ERROR: function tfp(integer[], anyelement) does not exist
CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N N P
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N P N
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -472,13 +503,13 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P N P
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
-DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P P N
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p,
@@ -1853,7 +1884,59 @@ returns anycompatiblerange as $$
select $1
$$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
+create function anyctest(anycompatible, anycompatiblemultirange)
+returns anycompatiblemultirange as $$
+ select $2
+$$ language sql;
+select x, pg_typeof(x) from anyctest(11, multirange(int4range(4,7))) x;
+ x | pg_typeof
+---------+----------------
+ {[4,7)} | int4multirange
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, multirange(numrange(4,7))) x;
+ x | pg_typeof
+---------+---------------
+ {[4,7)} | nummultirange
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, 12) x; -- fail
+ERROR: function anyctest(integer, integer) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11, 12) x;
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select x, pg_typeof(x) from anyctest(11.2, multirange(int4range(4,7))) x; -- fail
+ERROR: function anyctest(numeric, int4multirange) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11.2, multirange(int4ra...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select x, pg_typeof(x) from anyctest(11.2, '{[4,7)}') x; -- fail
+ERROR: could not identify anycompatiblemultirange type
+drop function anyctest(anycompatible, anycompatiblemultirange);
+create function anyctest(anycompatiblemultirange, anycompatiblemultirange)
+returns anycompatible as $$
+ select lower($1) + upper($2)
+$$ language sql;
+select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(int4range(4,7))) x;
+ x | pg_typeof
+----+-----------
+ 18 | integer
+(1 row)
+
+select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(numrange(4,7))) x; -- fail
+ERROR: function anyctest(int4multirange, nummultirange) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(multirange(int4range(11...
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyctest(anycompatiblemultirange, anycompatiblemultirange);
+-- fail, can't infer result type:
+create function anyctest(anycompatible)
+returns anycompatiblemultirange as $$
+ select $1
+$$ language sql;
+ERROR: cannot determine result data type
+DETAIL: A result of type anycompatiblemultirange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
create function anyctest(anycompatiblenonarray, anycompatiblenonarray)
returns anycompatiblearray as $$
select array[$1, $2]
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 7eced28452..71e2769f44 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -1556,7 +1556,7 @@ DROP FUNCTION dup(anyelement);
CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE FUNCTION dup (f1 anycompatible, f2 anycompatiblearray, f3 out anycompatible, f4 out anycompatiblearray)
AS 'select $1, $2' LANGUAGE sql;
SELECT dup(22, array[44]);
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index c421f5394f..e9f9e1a11b 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg
+---------------------
+
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg
+---------------------
+ [3,5)
+(1 row)
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1371,12 +1389,12 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function range_add_bounds(anyrange)
returns anyelement as 'select lower($1) + upper($1)' language sql;
select range_add_bounds(int4range(1, 17));
@@ -1430,7 +1448,7 @@ drop function anycompatiblearray_anycompatiblerange_func(anycompatiblearray, any
create function bogus_func(anycompatible)
returns anycompatiblerange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
--
-- Arrays of ranges
--
@@ -1508,6 +1526,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@@ -1525,6 +1544,16 @@ select * from outparam2_succeed(int4range(1,11));
{1,11} | {11,1}
(1 row)
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+ r | t
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@@ -1547,14 +1576,14 @@ select * from table_succeed(int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
-DETAIL: A result of type anyrange requires at least one input of type anyrange.
+DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 192445878d..d9ce961be2 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
+nummultirange_test|t
numrange_test|t
onek|t
onek2|t
@@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
+reservations|f
road|t
shighway|t
slow_emp4000|f
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 274130e706..8df1ae47e4 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -195,18 +195,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typinput
----------+-----------
+ typtype | typinput
+---------+---------------
c | record_in
d | domain_in
e | enum_in
+ m | multirange_in
r | range_in
-(4 rows)
+(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@@ -240,17 +241,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typoutput
----------+------------
+ typtype | typoutput
+---------+----------------
c | record_out
e | enum_out
+ m | multirange_out
r | range_out
-(3 rows)
+(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -318,18 +320,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
- typtype | typreceive
----------+-------------
+ typtype | typreceive
+---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
+ m | multirange_recv
r | range_recv
-(4 rows)
+(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@@ -363,17 +366,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
- typtype | typsend
----------+-------------
+ typtype | typsend
+---------+-----------------
c | record_send
e | enum_send
+ m | multirange_send
r | range_send
-(3 rows)
+(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -631,3 +635,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
+ rngtypid | rngsubtype | rngmultitypid
+----------+------------+---------------
+(0 rows)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 95f1925072..b0a02f8f71 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,6 +19,7 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
# ----------
test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes xid
@@ -27,7 +28,7 @@ test: strings numerology point lseg line box path polygon circle date time timet
# geometry depends on point, lseg, box, path, polygon and circle
# horology depends on interval, timetz, timestamp, timestamptz
# ----------
-test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions unicode
+test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions unicode multirangetypes
# ----------
# These four each depend on the previous one
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 8ba4136220..4a2a841c22 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -20,6 +20,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+ hash_multirange_extended(v, 0)::bit(32) as extended0,
+ hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+ ('{[5675, 550273)}'::int4multirange),
+ ('{[550274, 1550274)}'::int4multirange),
+ ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+ OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..842a7d091a
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,656 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select ' {} '::textmultirange;
+select ' { empty, empty } '::textmultirange;
+select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+select int4range(1, 3)::int4multirange;
+select int4range(1, null)::int4multirange;
+select int4range(null, null)::int4multirange;
+select 'empty'::textrange::textmultirange;
+select textrange('a', 'c')::textmultirange;
+select textrange('a', null)::textmultirange;
+select textrange(null, null)::textmultirange;
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT nummultirange() + nummultirange();
+SELECT nummultirange() + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT nummultirange() - nummultirange();
+SELECT nummultirange() - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+SELECT nummultirange() * nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT room_id, range_agg(booked_during)
+FROM reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT range_agg(r)
+FROM (VALUES
+ ('[a,c]'::textrange),
+ ('[b,b]'::textrange),
+ ('[c,f]'::textrange),
+ ('[g,h)'::textrange),
+ ('[h,j)'::textrange)
+ ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+ returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+ returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+ returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+ as $$ select upper($1) + $2[1] $$
+ language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
+
+create function anycompatiblearray_anycompatiblemultirange_func(a anycompatiblearray, mr anycompatiblemultirange)
+ returns anycompatible as 'select $1[1] + lower($2);' language sql;
+
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1,2], multirange(int4range(10,20)));
+
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1,2], multirange(numrange(10,20)));
+
+-- should fail
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1.1,2], multirange(int4range(10,20)));
+
+drop function anycompatiblearray_anycompatiblemultirange_func(anycompatiblearray, anycompatiblemultirange);
+
+create function anycompatiblerange_anycompatiblemultirange_func(r anycompatiblerange, mr anycompatiblemultirange)
+ returns anycompatible as 'select lower($1) + lower($2);' language sql;
+
+select anycompatiblerange_anycompatiblemultirange_func(int4range(1,2), multirange(int4range(10,20)));
+
+-- should fail
+select anycompatiblerange_anycompatiblemultirange_func(numrange(1,2), multirange(int4range(10,20)));
+
+drop function anycompatiblerange_anycompatiblemultirange_func(anycompatiblerange, anycompatiblemultirange);
+
+-- should fail
+create function bogus_func(anycompatible)
+ returns anycompatiblerange as 'select int4range(1,10)' language sql;
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+ (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+ (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off; -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+ as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+ as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+ as $$ select multirange($1), 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+ as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+ as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+ as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+ as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+ as $$ select $1, '[1,10]' $$ language sql;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 9a1ea3d999..69c4859102 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
+ if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+ if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+ then return true; end if;
+ end if;
return false;
end
$$ language plpgsql strict stable;
@@ -164,7 +168,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function. Likewise, ignore range constructor functions.
+-- dummy built-in function. Likewise, ignore range and multirange constructor
+-- functions.
SELECT DISTINCT p1.prorettype, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@@ -185,6 +192,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@@ -196,6 +205,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
+ p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+ p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@@ -279,16 +290,19 @@ WHERE p1.prorettype IN
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
- 'anyrange'::regtype = ANY (p1.proargtypes))
+ 'anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
--- anyrange is tighter than the rest, can only resolve from anyrange input
+-- anyrange and anymultirange are tighter than the rest, can only resolve
+-- from each other
SELECT p1.oid, p1.proname
FROM pg_proc as p1
-WHERE p1.prorettype = 'anyrange'::regtype
+WHERE p1.prorettype IN ('anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
- 'anyrange'::regtype = ANY (p1.proargtypes)
+ ('anyrange'::regtype = ANY (p1.proargtypes) OR
+ 'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- similarly for the anycompatible family
diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql
index e5222f1f81..ac52c387bb 100644
--- a/src/test/regress/sql/polymorphism.sql
+++ b/src/test/regress/sql/polymorphism.sql
@@ -71,6 +71,16 @@ select polyf(int4range(42, 49), 11, 4.5) as fail; -- range type doesn't fit
drop function polyf(x anycompatiblerange, y anycompatible, z anycompatible);
+create function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
+ select array[lower(x), upper(x), y, z]
+$$ language sql;
+
+select polyf(multirange(int4range(42, 49)), 11, 2::smallint) as int, polyf(multirange(float8range(4.5, 7.8)), 7.8, 11::real) as num;
+
+select polyf(multirange(int4range(42, 49)), 11, 4.5) as fail; -- range type doesn't fit
+
+drop function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible);
+
-- fail, can't infer type:
create function polyf(x anycompatible) returns anycompatiblerange as $$
select array[x + 1, x + 2]
@@ -84,6 +94,19 @@ select polyf(int4range(42, 49), array[11]) as int, polyf(float8range(4.5, 7.8),
drop function polyf(x anycompatiblerange, y anycompatiblearray);
+-- fail, can't infer type:
+create function polyf(x anycompatible) returns anycompatiblemultirange as $$
+ select array[x + 1, x + 2]
+$$ language sql;
+
+create function polyf(x anycompatiblemultirange, y anycompatiblearray) returns anycompatiblemultirange as $$
+ select x
+$$ language sql;
+
+select polyf(multirange(int4range(42, 49)), array[11]) as int, polyf(multirange(float8range(4.5, 7.8)), array[7]) as num;
+
+drop function polyf(x anycompatiblemultirange, y anycompatiblearray);
+
create function polyf(a anyelement, b anyarray,
c anycompatible, d anycompatible,
OUT x anyarray, OUT y anycompatiblearray)
@@ -994,6 +1017,35 @@ returns anycompatiblerange as $$
select $1
$$ language sql;
+create function anyctest(anycompatible, anycompatiblemultirange)
+returns anycompatiblemultirange as $$
+ select $2
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(11, multirange(int4range(4,7))) x;
+select x, pg_typeof(x) from anyctest(11, multirange(numrange(4,7))) x;
+select x, pg_typeof(x) from anyctest(11, 12) x; -- fail
+select x, pg_typeof(x) from anyctest(11.2, multirange(int4range(4,7))) x; -- fail
+select x, pg_typeof(x) from anyctest(11.2, '{[4,7)}') x; -- fail
+
+drop function anyctest(anycompatible, anycompatiblemultirange);
+
+create function anyctest(anycompatiblemultirange, anycompatiblemultirange)
+returns anycompatible as $$
+ select lower($1) + upper($2)
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(int4range(4,7))) x;
+select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(numrange(4,7))) x; -- fail
+
+drop function anyctest(anycompatiblemultirange, anycompatiblemultirange);
+
+-- fail, can't infer result type:
+create function anyctest(anycompatible)
+returns anycompatiblemultirange as $$
+ select $1
+$$ language sql;
+
create function anyctest(anycompatiblenonarray, anycompatiblenonarray)
returns anycompatiblearray as $$
select array[$1, $2]
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 4048b1d185..0276d19136 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -528,6 +532,7 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
+-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
@@ -539,6 +544,13 @@ create function outparam2_succeed(r anyrange, out lu anyarray, out ul anyarray)
select * from outparam2_succeed(int4range(1,11));
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+ as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index 4b492ce062..07879d9a57 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
- (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+ (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -153,7 +153,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -182,7 +182,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -234,7 +234,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@@ -263,7 +263,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@@ -467,3 +467,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 525d58e7f0..0b6a0717e9 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1362,6 +1362,9 @@ MultiXactMember
MultiXactOffset
MultiXactStateData
MultiXactStatus
+MultirangeIOData
+MultirangeParseState
+MultirangeType
MyData
NDBOX
NODE
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-07 20:19 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 16:33 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-03-14 18:13 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-17 04:52 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-19 20:41 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-19 20:43 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-20 05:24 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-23 23:32 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-24 04:23 ` Re: range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2020-04-04 23:10 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-04-11 03:44 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-04-11 16:36 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
@ 2020-04-11 16:42 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-04-11 16:56 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
1 sibling, 1 reply; 152+ messages in thread
From: Paul A Jungwirth @ 2020-04-11 16:42 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers; Jeff Davis <pgsql@j-davis.com>; Pavel Stehule <pavel.stehule@gmail.com>; David Fetter <david@fetter.org>
On Sat, Apr 11, 2020 at 9:36 AM Paul A Jungwirth
<pj@illuminatedcomputing.com> wrote:
> Btw I'm working on typanalyze + selectivity, and it seems like the
> test suite doesn't run those things?
Nevermind, I just had to add `analyze numrange_test` to
src/test/regress/sql/rangetypes.sql. :-) Do you want a separate patch
for that? Or maybe it should go in sql/vacuum.sql?
Regards,
Paul
^ permalink raw reply [nested|flat] 152+ messages in thread
* Re: range_agg
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-05 16:58 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:13 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Re: range_agg Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 17:43 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-01-04 05:29 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Re: range_agg Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Re: range_agg Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 05:57 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Re: range_agg Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-04 02:28 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-09 19:59 ` Re: range_agg Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-03-09 21:37 ` Tom Lane <tgl@sss.pgh.pa.us>
0 siblings, 0 replies; 152+ messages in thread
From: Tom Lane @ 2020-03-09 21:37 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Paul A Jungwirth <pj@illuminatedcomputing.com>; Pavel Stehule <pavel.stehule@gmail.com>; Jeff Davis <pgsql@j-davis.com>; pgsql-hackers
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> I wonder what's the point of multirange arrays. Is there a reason we
> create those?
That's what we thought about arrays of composites to start with,
too.
regards, tom lane
^ permalink raw reply [nested|flat] 152+ messages in thread
36 further messages in this thread omitted from this page (render size limit).
Use the per-message pages, or the t.mbox.gz byte stream, for the complete thread.
end of thread, other threads:[~2020-12-27 19:38 UTC | newest]
Thread overview: 152+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2019-05-03 22:56 range_agg Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-04 01:41 ` David Fetter <david@fetter.org>
2019-05-06 18:19 ` Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-06 22:07 ` David Fetter <david@fetter.org>
2019-05-06 23:21 ` Paul Jungwirth <pj@illuminatedcomputing.com>
2019-05-09 04:54 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-06-16 22:59 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-04 18:33 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2019-07-05 11:30 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-07-05 17:22 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 06:33 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-07-05 17:00 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-05-04 22:11 ` Corey Huinker <corey.huinker@gmail.com>
2019-05-06 18:17 ` Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-01 22:38 ` Jeff Davis <pgsql@j-davis.com>
2019-07-05 05:58 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-07-05 16:48 ` Jeff Davis <pgsql@j-davis.com>
2019-07-06 06:09 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-07-05 16:58 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-05 17:45 ` David Fetter <david@fetter.org>
2019-07-05 17:57 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-05 17:59 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-06 19:26 ` Jeff Davis <pgsql@j-davis.com>
2019-07-06 19:13 ` Jeff Davis <pgsql@j-davis.com>
2019-07-08 16:46 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 05:08 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-07-09 16:31 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 18:24 ` Jeff Davis <pgsql@j-davis.com>
2019-07-09 19:10 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-07-09 19:23 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-07-10 04:26 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-10 04:50 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-07-09 15:51 ` David Fetter <david@fetter.org>
2019-07-09 16:40 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-10 06:24 ` David Fetter <david@fetter.org>
2019-07-10 14:55 ` Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 19:01 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2019-07-09 19:05 ` Paul Jungwirth <pj@illuminatedcomputing.com>
2019-07-09 19:02 ` Jeff Davis <pgsql@j-davis.com>
2019-07-10 04:18 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-17 17:47 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-21 05:33 ` Jeff Davis <pgsql@j-davis.com>
2019-08-22 04:54 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-26 18:34 ` Jeff Davis <pgsql@j-davis.com>
2019-09-01 13:26 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 17:15 ` Jeff Davis <pgsql@j-davis.com>
2019-09-05 17:45 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-05 18:52 ` Jeff Davis <pgsql@j-davis.com>
2019-09-22 04:50 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-09-26 21:13 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2019-11-06 23:02 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-07 02:35 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-19 09:16 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-11-20 05:49 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-20 19:32 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-21 09:06 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-11-21 20:15 ` Paul Jungwirth <pj@illuminatedcomputing.com>
2019-11-22 05:21 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-11-22 16:25 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-11-22 16:57 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-11-30 02:10 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 17:43 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 18:19 ` Pavel Stehule <pavel.stehule@gmail.com>
2019-12-20 19:20 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-12-20 19:29 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2019-12-20 21:15 ` Tom Lane <tgl@sss.pgh.pa.us>
2020-01-04 05:29 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-10 09:38 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-01-17 20:07 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 15:19 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-01-18 16:07 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-18 16:35 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 08:09 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-01-19 23:34 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-20 00:38 ` Tom Lane <tgl@sss.pgh.pa.us>
2020-01-20 04:49 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-01-20 05:57 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-21 23:54 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-01-24 07:08 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-03-04 02:28 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-04 21:33 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-04 22:26 ` Paul Jungwirth <pj@illuminatedcomputing.com>
2020-03-07 20:19 ` Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:06 ` Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:20 ` Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 21:26 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-03-08 05:27 ` Isaac Morland <isaac.morland@gmail.com>
2020-03-08 16:56 ` Tom Lane <tgl@sss.pgh.pa.us>
2020-03-07 22:13 ` David Fetter <david@fetter.org>
2020-03-07 23:45 ` Tom Lane <tgl@sss.pgh.pa.us>
2020-03-08 02:45 ` David Fetter <david@fetter.org>
2020-03-09 17:53 ` Robert Haas <robertmhaas@gmail.com>
2020-03-10 01:34 ` Jeff Davis <pgsql@j-davis.com>
2020-03-10 16:08 ` David Fetter <david@fetter.org>
2020-03-11 23:04 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-12 12:38 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-13 16:32 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 16:33 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-13 17:06 ` Tom Lane <tgl@sss.pgh.pa.us>
2020-03-13 21:39 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-14 17:34 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-20 14:48 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-14 18:13 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-17 04:52 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-19 20:41 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-19 20:43 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-20 05:24 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-03-23 23:32 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-24 04:23 ` Paul Jungwirth <pj@illuminatedcomputing.com>
2020-04-04 23:10 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-04-05 22:13 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-04-05 22:14 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-04-11 03:44 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-04-11 16:36 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-04-11 16:42 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-04-11 16:56 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-07-05 17:20 ` Justin Pryzby <pryzby@telsasoft.com>
2020-07-05 19:11 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-07-05 19:25 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-08-16 19:55 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-09-24 00:04 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-10-09 06:51 ` Pavel Stehule <pavel.stehule@gmail.com>
2020-11-27 08:35 ` Alexander Korotkov <aekorotkov@gmail.com>
2020-11-29 17:11 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-11-29 19:43 ` Alexander Korotkov <aekorotkov@gmail.com>
2020-11-29 20:53 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2020-11-30 19:35 ` Alexander Korotkov <aekorotkov@gmail.com>
2020-11-30 20:39 ` Alexander Korotkov <aekorotkov@gmail.com>
2020-12-07 23:45 ` Alexander Korotkov <aekorotkov@gmail.com>
2020-12-08 00:00 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2020-12-08 00:20 ` Alexander Korotkov <aekorotkov@gmail.com>
2020-12-15 23:21 ` Alexander Korotkov <aekorotkov@gmail.com>
2020-12-16 04:14 ` Alexander Korotkov <aekorotkov@gmail.com>
2020-12-17 19:10 ` Alexander Korotkov <aekorotkov@gmail.com>
2020-12-27 09:50 ` Alexander Korotkov <aekorotkov@gmail.com>
2020-12-27 17:53 ` Zhihong Yu <zyu@yugabyte.com>
2020-12-27 18:07 ` David Fetter <david@fetter.org>
2020-12-27 19:38 ` Alexander Korotkov <aekorotkov@gmail.com>
2020-12-27 19:31 ` Alexander Korotkov <aekorotkov@gmail.com>
2020-03-09 19:59 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-09 21:37 ` Tom Lane <tgl@sss.pgh.pa.us>
2019-12-20 19:13 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2020-03-08 17:28 ` David G. Johnston <david.g.johnston@gmail.com>
2019-07-05 17:38 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-07-23 22:32 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2019-07-24 05:13 ` Paul A Jungwirth <pj@illuminatedcomputing.com>
2019-08-01 08:34 ` Thomas Munro <thomas.munro@gmail.com>
2020-12-16 21:54 range_agg Zhihong Yu <zyu@yugabyte.com>
2020-12-16 22:03 ` Alexander Korotkov <aekorotkov@gmail.com>
2020-12-16 23:34 ` Alexander Korotkov <aekorotkov@gmail.com>
2020-12-16 23:37 ` Zhihong Yu <zyu@yugabyte.com>
2020-12-16 23:41 ` Alexander Korotkov <aekorotkov@gmail.com>
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox