-<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.459 2008/11/13 23:01:09 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.460 2008/11/14 00:51:46 tgl Exp $ -->
<chapter id="functions">
<title>Functions and Operators</title>
<entry><literal>string_to_array('xx~^~yy~^~zz', '~^~')</literal></entry>
<entry><literal>{xx,yy,zz}</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>unnest</function>(<type>anyarray</type>)
+ </literal>
+ </entry>
+ <entry><type>setof anyelement</type></entry>
+ <entry>expand an array to a set of rows</entry>
+ <entry><literal>unnest(ARRAY[1,2])</literal></entry>
+ <entry><literal>1</literal><para><literal>2</literal></para> (2 rows)</entry>
+ </row>
</tbody>
</tgroup>
</table>
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
+ }
+}
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200811131
+#define CATALOG_VERSION_NO 200811132
#endif
* 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
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_ ));
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
(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)
+
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[]);