aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/arrayfuncs.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2008-11-14 00:51:47 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2008-11-14 00:51:47 +0000
commitc889ebce0aa5f848d680547e3af0aad8b9e577a7 (patch)
treef3c16122152d35861e50bfdbfc11ab998d00f290 /src/backend/utils/adt/arrayfuncs.c
parent5b9453bcd3b28561c33495199c61c81994c58398 (diff)
downloadpostgresql-c889ebce0aa5f848d680547e3af0aad8b9e577a7.tar.gz
postgresql-c889ebce0aa5f848d680547e3af0aad8b9e577a7.zip
Implement the basic form of UNNEST, ie unnest(anyarray) returns setof
anyelement. This lacks the WITH ORDINALITY option, as well as the multiple input arrays option added in the most recent SQL specs. But it's still a pretty useful subset of the spec's functionality, and it is enough to allow obsoleting contrib/intagg.
Diffstat (limited to 'src/backend/utils/adt/arrayfuncs.c')
-rw-r--r--src/backend/utils/adt/arrayfuncs.c106
1 files changed, 105 insertions, 1 deletions
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 9d2b036897b..4580040d697 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.149 2008/11/12 13:09:27 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.150 2008/11/14 00:51:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -4635,3 +4635,107 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs,
return result;
}
+
+
+/*
+ * UNNEST
+ */
+Datum
+array_unnest(PG_FUNCTION_ARGS)
+{
+ typedef struct
+ {
+ ArrayType *arr;
+ int nextelem;
+ int numelems;
+ char *elemdataptr; /* this moves with nextelem */
+ bits8 *arraynullsptr; /* this does not */
+ int16 elmlen;
+ bool elmbyval;
+ char elmalign;
+ } array_unnest_fctx;
+
+ FuncCallContext *funcctx;
+ array_unnest_fctx *fctx;
+ MemoryContext oldcontext;
+
+ /* stuff done only on the first call of the function */
+ if (SRF_IS_FIRSTCALL())
+ {
+ ArrayType *arr = PG_GETARG_ARRAYTYPE_P(0);
+
+ /* 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);
+
+ /* allocate memory for user context */
+ fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
+
+ /*
+ * Initialize state. Note we assume that the originally passed
+ * array will stick around for the whole call series.
+ */
+ fctx->arr = arr;
+ fctx->nextelem = 0;
+ fctx->numelems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
+
+ fctx->elemdataptr = ARR_DATA_PTR(arr);
+ fctx->arraynullsptr = ARR_NULLBITMAP(arr);
+
+ get_typlenbyvalalign(ARR_ELEMTYPE(arr),
+ &fctx->elmlen,
+ &fctx->elmbyval,
+ &fctx->elmalign);
+
+ funcctx->user_fctx = fctx;
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ /* stuff done on every call of the function */
+ funcctx = SRF_PERCALL_SETUP();
+ fctx = funcctx->user_fctx;
+
+ if (fctx->nextelem < fctx->numelems)
+ {
+ int offset = fctx->nextelem++;
+ Datum elem;
+
+ /*
+ * Check for NULL array element
+ */
+ if (array_get_isnull(fctx->arraynullsptr, offset))
+ {
+ fcinfo->isnull = true;
+ elem = (Datum) 0;
+ /* elemdataptr does not move */
+ }
+ else
+ {
+ /*
+ * OK, get the element
+ */
+ char *ptr = fctx->elemdataptr;
+
+ fcinfo->isnull = false;
+ elem = ArrayCast(ptr, fctx->elmbyval, fctx->elmlen);
+
+ /*
+ * Advance elemdataptr over it
+ */
+ ptr = att_addlength_pointer(ptr, fctx->elmlen, ptr);
+ ptr = (char *) att_align_nominal(ptr, fctx->elmalign);
+ fctx->elemdataptr = ptr;
+ }
+
+ SRF_RETURN_NEXT(funcctx, elem);
+ }
+ else
+ {
+ /* do when there is no more left */
+ SRF_RETURN_DONE(funcctx);
+ }
+}