diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/commands/typecmds.c | 92 | ||||
-rw-r--r-- | src/backend/utils/adt/multirangetypes.c | 106 |
2 files changed, 190 insertions, 8 deletions
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 58ec65c6afc..9ea0ce17e12 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -114,7 +114,11 @@ 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); + Oid rangeArrayOid, + Oid *oneArgContructorOid); +static void makeMultirangeCasts(const char *name, Oid namespace, + Oid multirangeOid, Oid rangeOid, + Oid rangeArrayOid, Oid singleArgContructorOid); static Oid findTypeInputFunction(List *procname, Oid typeOid); static Oid findTypeOutputFunction(List *procname, Oid typeOid); static Oid findTypeReceiveFunction(List *procname, Oid typeOid); @@ -1365,7 +1369,7 @@ DefineRange(CreateRangeStmt *stmt) ListCell *lc; ObjectAddress address; ObjectAddress mltrngaddress PG_USED_FOR_ASSERTS_ONLY; - Oid castFuncOid; + Oid singleArgContructorOid; /* Convert list of names to a name and namespace */ typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName, @@ -1717,10 +1721,12 @@ DefineRange(CreateRangeStmt *stmt) makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype); makeMultirangeConstructors(multirangeTypeName, typeNamespace, multirangeOid, typoid, rangeArrayOid, - &castFuncOid); + &singleArgContructorOid); - /* Create cast from the range type to its multirange type */ - CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL); + /* Create casts for this multirange type */ + makeMultirangeCasts(multirangeTypeName, typeNamespace, + multirangeOid, typoid, rangeArrayOid, + singleArgContructorOid); pfree(multirangeTypeName); pfree(multirangeArrayName); @@ -1808,13 +1814,13 @@ makeRangeConstructors(const char *name, Oid namespace, * 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 + * Sets oneArgContructorOid 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) + Oid *oneArgContructorOid) { ObjectAddress myself, referenced; @@ -1904,7 +1910,7 @@ makeMultirangeConstructors(const char *name, Oid namespace, /* ditto */ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); pfree(argtypes); - *castFuncOid = myself.objectId; + *oneArgContructorOid = myself.objectId; /* n-arg constructor - vararg */ argtypes = buildoidvector(&rangeArrayOid, 1); @@ -1950,6 +1956,76 @@ makeMultirangeConstructors(const char *name, Oid namespace, } /* + * Create casts for the multirange type. The first cast makes multirange from + * range, and it's based on the single-argument constructor. The second cast + * makes an array of ranges from multirange. + */ +static void +makeMultirangeCasts(const char *name, Oid namespace, + Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid, + Oid singleArgContructorOid) +{ + ObjectAddress myself, + referenced; + oidvector *argtypes; + + /* + * Create cast from range to multirange using the existing single-argument + * constructor procedure. + */ + CastCreate(rangeOid, multirangeOid, singleArgContructorOid, 'e', 'f', + DEPENDENCY_INTERNAL); + + referenced.classId = TypeRelationId; + referenced.objectId = multirangeOid; + referenced.objectSubId = 0; + + /* multirange_to_array() function */ + argtypes = buildoidvector(&multirangeOid, 1); + myself = ProcedureCreate("multirange_to_array", /* name */ + namespace, + false, /* replace */ + false, /* returns set */ + rangeArrayOid, /* return type */ + BOOTSTRAP_SUPERUSERID, /* proowner */ + INTERNALlanguageId, /* language */ + F_FMGR_INTERNAL_VALIDATOR, + "multirange_to_array", /* prosrc */ + NULL, /* probin */ + NULL, /* prosqlbody */ + 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 */ + + /* + * Make the multirange_to_array() function internally-dependent on the + * multirange type so that they go away silently when the type is dropped. + */ + recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); + pfree(argtypes); + + /* + * Create cast from multirange to the array of ranges using + * multirange_to_array() function. + */ + CastCreate(multirangeOid, rangeArrayOid, myself.objectId, 'e', 'f', + DEPENDENCY_INTERNAL); +} + +/* * Find suitable I/O and other support functions for a type. * * typeOid is the type's OID (which will already exist, if only as a shell diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c index fbcc27d0726..fbbda4af36d 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" @@ -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 */ |