aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/adt/arrayfuncs.c106
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/catalog/pg_proc.h4
-rw-r--r--src/include/utils/array.h3
-rw-r--r--src/test/regress/expected/arrays.out62
-rw-r--r--src/test/regress/sql/arrays.sql7
6 files changed, 181 insertions, 5 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);
+ }
+}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 1f3d9c6485c..dd1bfbaf45f 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.505 2008/11/13 15:59:50 petere Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.506 2008/11/14 00:51:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200811131
+#define CATALOG_VERSION_NO 200811132
#endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 5c01d1b3701..56b7f6786aa 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.527 2008/11/13 15:59:50 petere Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.528 2008/11/14 00:51:46 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@@ -1022,6 +1022,8 @@ DATA(insert OID = 1193 ( array_fill PGNSP PGUID 12 1 0 0 f f f f i 2 2277 "2283
DESCR("array constructor with value");
DATA(insert OID = 1286 ( array_fill PGNSP PGUID 12 1 0 0 f f f f i 3 2277 "2283 1007 1007" _null_ _null_ _null_ array_fill_with_lower_bounds _null_ _null_ _null_ ));
DESCR("array constructor with value");
+DATA(insert OID = 2331 ( unnest PGNSP PGUID 12 1 100 0 f f t t i 1 2283 "2277" _null_ _null_ _null_ array_unnest _null_ _null_ _null_ ));
+DESCR("expand array to set of rows");
DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 f f f f i 2 2281 "2281 2283" _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("array_agg transition function");
DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 f f f f i 1 2277 "2281" _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 8b6ef08b276..8a7f10451f0 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -49,7 +49,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.71 2008/11/13 15:59:50 petere Exp $
+ * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.72 2008/11/14 00:51:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -206,6 +206,7 @@ extern Datum generate_subscripts(PG_FUNCTION_ARGS);
extern Datum generate_subscripts_nodir(PG_FUNCTION_ARGS);
extern Datum array_fill(PG_FUNCTION_ARGS);
extern Datum array_fill_with_lower_bounds(PG_FUNCTION_ARGS);
+extern Datum array_unnest(PG_FUNCTION_ARGS);
extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
int arraytyplen, int elmlen, bool elmbyval, char elmalign,
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 1e990aff732..aecc74c5c4b 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -1161,3 +1161,65 @@ select array_agg(unique1) from tenk1 where unique1 < -15;
(1 row)
+select unnest(array[1,2,3]);
+ unnest
+--------
+ 1
+ 2
+ 3
+(3 rows)
+
+select * from unnest(array[1,2,3]);
+ unnest
+--------
+ 1
+ 2
+ 3
+(3 rows)
+
+select unnest(array[1,2,3,4.5]::float8[]);
+ unnest
+--------
+ 1
+ 2
+ 3
+ 4.5
+(4 rows)
+
+select unnest(array[1,2,3,4.5]::numeric[]);
+ unnest
+--------
+ 1
+ 2
+ 3
+ 4.5
+(4 rows)
+
+select unnest(array[1,2,3,null,4,null,null,5,6]);
+ unnest
+--------
+ 1
+ 2
+ 3
+
+ 4
+
+
+ 5
+ 6
+(9 rows)
+
+select unnest(array[1,2,3,null,4,null,null,5,6]::text[]);
+ unnest
+--------
+ 1
+ 2
+ 3
+
+ 4
+
+
+ 5
+ 6
+(9 rows)
+
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 586f65c2dd6..fc72f29f602 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -402,3 +402,10 @@ select array_agg(nullif(ten, 4)) from tenk1 where unique1 < 15;
select cardinality(array_agg(unique1)) from tenk1 where unique1 < 15;
select array_agg(unique1) from (select * from tenk1 order by unique1 asc) as tab where unique1 < 15;
select array_agg(unique1) from tenk1 where unique1 < -15;
+
+select unnest(array[1,2,3]);
+select * from unnest(array[1,2,3]);
+select unnest(array[1,2,3,4.5]::float8[]);
+select unnest(array[1,2,3,4.5]::numeric[]);
+select unnest(array[1,2,3,null,4,null,null,5,6]);
+select unnest(array[1,2,3,null,4,null,null,5,6]::text[]);