aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlexander Korotkov <akorotkov@postgresql.org>2021-07-18 21:07:24 +0300
committerAlexander Korotkov <akorotkov@postgresql.org>2021-07-18 21:11:33 +0300
commit244ad5415557812a6ac4dc5d6e2ae908361d82c3 (patch)
tree6382563ed9e2ca0d971be26dd35d4afb822f2dfb /src
parentd8f3b021c618bf58c19f88fb86476793fd650336 (diff)
downloadpostgresql-244ad5415557812a6ac4dc5d6e2ae908361d82c3.tar.gz
postgresql-244ad5415557812a6ac4dc5d6e2ae908361d82c3.zip
Support for unnest(multirange)
It has been spotted that multiranges lack of ability to decompose them into individual ranges. Subscription and proper expanded object representation require substantial work, and it's too late for v14. This commit provides the implementation of unnest(multirange), which is quite trivial. unnest(multirange) is defined as a polymorphic procedure. Catversion is bumped. Reported-by: Jonathan S. Katz Discussion: https://postgr.es/m/flat/60258efe-bd7e-4886-82e1-196e0cac5433%40postgresql.org Author: Alexander Korotkov Reviewed-by: Justin Pryzby, Jonathan S. Katz, Zhihong Yu, Tom Lane Reviewed-by: Alvaro Herrera
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/adt/multirangetypes.c73
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_proc.dat4
-rw-r--r--src/test/regress/expected/multirangetypes.out24
-rw-r--r--src/test/regress/sql/multirangetypes.sql7
5 files changed, 109 insertions, 1 deletions
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index 7aeec7617fc..7076a60c32d 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -34,6 +34,7 @@
#include "access/tupmacs.h"
#include "common/hashfn.h"
+#include "funcapi.h"
#include "lib/stringinfo.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
@@ -2645,6 +2646,78 @@ range_merge_from_multirange(PG_FUNCTION_ARGS)
PG_RETURN_RANGE_P(result);
}
+/* Turn multirange into a set of ranges */
+Datum
+multirange_unnest(PG_FUNCTION_ARGS)
+{
+ typedef struct
+ {
+ MultirangeType *mr;
+ TypeCacheEntry *typcache;
+ int index;
+ } multirange_unnest_fctx;
+
+ FuncCallContext *funcctx;
+ multirange_unnest_fctx *fctx;
+ MemoryContext oldcontext;
+
+ /* stuff done only on the first call of the function */
+ if (SRF_IS_FIRSTCALL())
+ {
+ MultirangeType *mr;
+
+ /* create a function context for cross-call persistence */
+ funcctx = SRF_FIRSTCALL_INIT();
+
+ /*
+ * switch to memory context appropriate for multiple function calls
+ */
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+ /*
+ * Get the multirange value and detoast if needed. We can't do this
+ * earlier because if we have to detoast, we want the detoasted copy
+ * to be in multi_call_memory_ctx, so it will go away when we're done
+ * and not before. (If no detoast happens, we assume the originally
+ * passed multirange will stick around till then.)
+ */
+ mr = PG_GETARG_MULTIRANGE_P(0);
+
+ /* allocate memory for user context */
+ fctx = (multirange_unnest_fctx *) palloc(sizeof(multirange_unnest_fctx));
+
+ /* initialize state */
+ fctx->mr = mr;
+ fctx->index = 0;
+ fctx->typcache = lookup_type_cache(MultirangeTypeGetOid(mr),
+ TYPECACHE_MULTIRANGE_INFO);
+
+ funcctx->user_fctx = fctx;
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ /* stuff done on every call of the function */
+ funcctx = SRF_PERCALL_SETUP();
+ fctx = funcctx->user_fctx;
+
+ if (fctx->index < fctx->mr->rangeCount)
+ {
+ RangeType *range;
+
+ range = multirange_get_range(fctx->typcache->rngtype,
+ fctx->mr,
+ fctx->index);
+ fctx->index++;
+
+ SRF_RETURN_NEXT(funcctx, RangeTypePGetDatum(range));
+ }
+ else
+ {
+ /* do when there is no more left */
+ SRF_RETURN_DONE(funcctx);
+ }
+}
+
/* Hash support */
/* hash a multirange value */
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 1b23c7c253b..0a4f4abdb39 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202106151
+#define CATALOG_VERSION_NO 202107181
#endif
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index fde251fa4f3..79669bf5a2e 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10537,6 +10537,10 @@
proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
prorettype => 'anymultirange', proargtypes => 'anymultirange',
prosrc => 'aggregate_dummy' },
+{ oid => '1293', descr => 'expand multirange to set of ranges',
+ proname => 'unnest', prorows => '100',
+ proretset => 't', prorettype => 'anyrange', proargtypes => 'anymultirange',
+ prosrc => 'multirange_unnest' },
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
index af3ef4a258c..7f689ad41b4 100644
--- a/src/test/regress/expected/multirangetypes.out
+++ b/src/test/regress/expected/multirangetypes.out
@@ -347,6 +347,23 @@ select textrange(null, null)::textmultirange;
(1 row)
--
+-- test unnest(multirange) function
+--
+select unnest(int4multirange(int4range('5', '6'), int4range('1', '2')));
+ unnest
+--------
+ [1,2)
+ [5,6)
+(2 rows)
+
+select unnest(textmultirange(textrange('a', 'b'), textrange('d', 'e')));
+ unnest
+--------
+ [a,b)
+ [d,e)
+(2 rows)
+
+--
-- create some test data and test the operators
--
CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
@@ -2938,6 +2955,13 @@ LINE 1: select multirange_of_text(textrange2('a','Z'));
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
select multirange_of_text(textrange1('a','Z')) @> 'b'::text;
ERROR: range lower bound must be less than or equal to range upper bound
+select unnest(multirange_of_text(textrange1('a','b'), textrange1('d','e')));
+ unnest
+--------
+ [a,b)
+ [d,e)
+(2 rows)
+
select _textrange1(textrange2('a','z')) @> 'b'::text;
?column?
----------
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
index b91a23e0d5a..ad50afc0f55 100644
--- a/src/test/regress/sql/multirangetypes.sql
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -78,6 +78,12 @@ select textrange('a', null)::textmultirange;
select textrange(null, null)::textmultirange;
--
+-- test unnest(multirange) function
+--
+select unnest(int4multirange(int4range('5', '6'), int4range('1', '2')));
+select unnest(textmultirange(textrange('a', 'b'), textrange('d', 'e')));
+
+--
-- create some test data and test the operators
--
@@ -658,6 +664,7 @@ create type textrange2 as range(subtype=text, multirange_type_name=_textrange1,
select multirange_of_text(textrange2('a','Z')); -- should fail
select multirange_of_text(textrange1('a','Z')) @> 'b'::text;
+select unnest(multirange_of_text(textrange1('a','b'), textrange1('d','e')));
select _textrange1(textrange2('a','z')) @> 'b'::text;
drop type textrange1;