mirror of
https://github.com/postgres/postgres.git
synced 2025-08-31 17:02:12 +03:00
Support for unnest(multirange) and cast multirange as an array of ranges
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) and cast multirange as an array of ranges, which is quite trivial. unnest(multirange) is defined as a polymorphic procedure. The catalog description of the cast underlying procedure is duplicated for each multirange type because we don't have anyrangearray polymorphic type to use here. 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
This commit is contained in:
@@ -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"
|
||||
@@ -1068,6 +1069,39 @@ multirange_constructor0(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
|
||||
}
|
||||
|
||||
/*
|
||||
* Cast multirange to an array of ranges.
|
||||
*/
|
||||
Datum
|
||||
multirange_to_array(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayBuildState *astate = NULL;
|
||||
MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
|
||||
TypeCacheEntry *typcache;
|
||||
int i;
|
||||
|
||||
typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
|
||||
|
||||
astate = initArrayResult(typcache->rngtype->type_id,
|
||||
CurrentMemoryContext,
|
||||
false);
|
||||
|
||||
for (i = 0; i < mr->rangeCount; i++)
|
||||
{
|
||||
RangeType *r;
|
||||
|
||||
r = multirange_get_range(typcache->rngtype, mr, i);
|
||||
astate = accumArrayResult(astate,
|
||||
RangeTypePGetDatum(r),
|
||||
false,
|
||||
typcache->rngtype->type_id,
|
||||
CurrentMemoryContext);
|
||||
}
|
||||
|
||||
PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* multirange, multirange -> multirange type functions */
|
||||
|
||||
@@ -2645,6 +2679,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 */
|
||||
|
Reference in New Issue
Block a user