diff options
author | Tom Lane | 2008-11-14 00:51:47 +0000 |
---|---|---|
committer | Tom Lane | 2008-11-14 00:51:47 +0000 |
commit | d585c8d5674615eceeca493e22c4dcab19b11155 (patch) | |
tree | 7d2f7d5ea07dea6535f1d7dc85282cfe70e309c0 | |
parent | bd1e201e16641c2a47b99d9e75c22d0dbee87780 (diff) |
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.
-rw-r--r-- | doc/src/sgml/func.sgml | 11 | ||||
-rw-r--r-- | src/backend/utils/adt/arrayfuncs.c | 104 | ||||
-rw-r--r-- | src/include/catalog/catversion.h | 2 | ||||
-rw-r--r-- | src/include/catalog/pg_proc.h | 2 | ||||
-rw-r--r-- | src/include/utils/array.h | 1 | ||||
-rw-r--r-- | src/test/regress/expected/arrays.out | 62 | ||||
-rw-r--r-- | src/test/regress/sql/arrays.sql | 7 |
7 files changed, 188 insertions, 1 deletions
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 73a794c7f6..c5c30ae870 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -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> diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 4b32c7a762..38149a5b14 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -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 9e53282685..6e3a0e5983 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -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 be1fd19996..5c79ee2f4a 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -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 6a032bf1c1..3c6d7921ba 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -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 1e990aff73..aecc74c5c4 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 586f65c2dd..fc72f29f60 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[]); |