Implement the basic form of UNNEST, ie unnest(anyarray) returns setof
authorTom Lane
Fri, 14 Nov 2008 00:51:47 +0000 (00:51 +0000)
committerTom Lane
Fri, 14 Nov 2008 00:51:47 +0000 (00:51 +0000)
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.

doc/src/sgml/func.sgml
src/backend/utils/adt/arrayfuncs.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/utils/array.h
src/test/regress/expected/arrays.out
src/test/regress/sql/arrays.sql

index aee74366b01f0b2ed5640f29598bb10b2608a2a6..67500340b0ffa0c7dec8f54f14762773ca2a2b20 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
  
   Functions and Operators
@@ -9482,6 +9482,17 @@ SELECT NULLIF(value, '(none)') ...
         string_to_array('xx~^~yy~^~zz', '~^~')
         {xx,yy,zz}
        
+       
+        
+         
+          unnest(anyarray)
+         
+        
+        setof anyelement
+        expand an array to a set of rows
+        unnest(ARRAY[1,2])
+        12 (2 rows)
+       
       
      
     
index 9d2b036897be76188e3bd82d0444835005f5f4ec..4580040d697491a21e57e75660611a4e1cc9cbd2 100644 (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);
+   }
+}
index 1f3d9c6485cd835d1f2711ad5be90bc24d043d53..dd1bfbaf45ff19ee8d29526a5328259fedb10444 100644 (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
index 5c01d1b370160d656ebf11e116324e1228711ec4..56b7f6786aa42354bfdd26e78ae710138a08cb15 100644 (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_ ));
index 8b6ef08b276f5bdea8c474d5c5c350589d11f303..8a7f10451f0e7c3d5e70579d311a57df4dfde220 100644 (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,
index 1e990aff73220ca5911d89c076bf12c494d37478..aecc74c5c4bf3dec2b151302f14234939887f51b 100644 (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)
+
index 586f65c2dd670de87c955a1370a9a0ea8cf8077a..fc72f29f602ca2445255c1b34f0132a147f77d28 100644 (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[]);