OSDN Git Service

Implement the basic form of UNNEST, ie unnest(anyarray) returns setof
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 14 Nov 2008 00:51:47 +0000 (00:51 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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 aee7436..6750034 100644 (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>
index 9d2b036..4580040 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 1f3d9c6..dd1bfba 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 5c01d1b..56b7f67 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 8b6ef08..8a7f104 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 1e990af..aecc74c 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 586f65c..fc72f29 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[]);