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.
This commit is contained in:
Tom Lane 2008-11-14 00:51:47 +00:00
parent 5b9453bcd3
commit c889ebce0a
7 changed files with 193 additions and 6 deletions

View File

@ -1,4 +1,4 @@
<!-- $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>
@ -9482,6 +9482,17 @@ SELECT NULLIF(value, '(none)') ...
<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>

View File

@ -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);
}
}

View File

@ -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

View File

@ -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_ ));

View File

@ -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,

View File

@ -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)

View File

@ -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[]);