*** ./doc/src/sgml/func.sgml.orig	2008-03-24 18:01:54.000000000 +0100
--- ./doc/src/sgml/func.sgml	2008-03-24 19:03:43.000000000 +0100
***************
*** 10562,10569 ****
  
    <para>
     This section describes functions that possibly return more than one row.
-    Currently the only functions in this class are series generating functions,
-    as detailed in <xref linkend="functions-srf-series">.
    </para>
  
    <table id="functions-srf-series">
--- 10562,10567 ----
***************
*** 10641,10646 ****
--- 10639,10697 ----
  (3 rows)
  </programlisting>
    </para>
+ 
+   <table id="functions-srf-subscripts">
+    <title>Subscripts Generating Functions</title>
+    <tgroup cols="3">
+     <thead>
+      <row>
+       <entry>Function</entry>
+       <entry>Return Type</entry>
+       <entry>Description</entry>
+      </row>
+     </thead>
+ 
+     <tbody>
+      <row>
+       <entry><literal><function>generate_subscripts</function>(<parameter>array annyarray</parameter>, <parameter>dim int</parameter>)</literal></entry>
+       <entry><type>setof int</type></entry>
+       <entry>
+        Generate a series of array's subscripts.
+       </entry>
+      </row>
+ 
+      <row>
+       <entry><literal><function>generate_subscripts</function>(<parameter>array annyarray</parameter>, <parameter>dim int</parameter>, <parameter>reverse boolean</parameter>)</literal></entry>
+       <entry><type>setof int</type></entry>
+       <entry>
+        Generate a series of array's subscripts. When <parameter>reverse</parameter> is true, then series is reverse order.  
+       </entry>
+      </row>
+ 
+     </tbody>
+    </tgroup>
+   </table>
+ 
+ <programlisting>
+ -- unnest 2D array
+ create or replace function unnest2(anyarray)
+ returns setof anyelement as $$
+ select $1[i][j] 
+    from generate_subscripts($1,1) g1(i),
+         generate_subscripts($1,2) g2(j);
+ $$ language sql immutable;
+ CREATE FUNCTION
+ postgres=# select * from unnest2(array[[1,2],[3,4]]);
+  unnest2 
+ ---------
+        1
+        2
+        3
+        4
+ (4 rows)
+ </programlisting>
+ 
+ 
   </sect1>
  
   <sect1 id="functions-info">
*** ./src/backend/utils/adt/arrayfuncs.c.orig	2008-03-24 17:38:45.000000000 +0100
--- ./src/backend/utils/adt/arrayfuncs.c	2008-03-24 18:12:35.000000000 +0100
***************
*** 16,21 ****
--- 16,22 ----
  
  #include <ctype.h>
  
+ #include "funcapi.h"
  #include "access/tupmacs.h"
  #include "libpq/pqformat.h"
  #include "parser/parse_coerce.h"
***************
*** 96,101 ****
--- 97,108 ----
  				   int typlen, bool typbyval, char typalign);
  static int	array_cmp(FunctionCallInfo fcinfo);
  
+ typedef struct generate_subscripts_fctx
+ {
+         int4    lower;
+         int4    upper;
+         bool    reverse;
+ } generate_subscripts_fctx;
  
  /*
   * array_in :
***************
*** 4237,4239 ****
--- 4244,4315 ----
  
  	PG_RETURN_ARRAYTYPE_P(result);
  }
+ 
+ /* 
+  * array_subscripts(array anyarray, dim int, reverse bool)
+  *   returns all subscripts of array for any dimension
+  */
+ Datum
+ array_subscripts_direction(PG_FUNCTION_ARGS)
+ {
+ 	return array_subscripts(fcinfo);
+ }
+ 
+ Datum
+ array_subscripts(PG_FUNCTION_ARGS)
+ {
+         FuncCallContext *funcctx;
+         MemoryContext oldcontext;
+         generate_subscripts_fctx *fctx;
+ 
+         /* stuff done only on the first call of the function */
+         if (SRF_IS_FIRSTCALL())
+         {
+                 ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
+                 int reqdim = PG_GETARG_INT32(1);
+                 int     *lb, *dimv;
+ 
+                 /* create a function context for cross-call persistence */
+                 funcctx = SRF_FIRSTCALL_INIT();
+ 
+                 /* Sanity check: does it look like an array at all? */
+                 if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
+                         SRF_RETURN_DONE(funcctx);
+ 
+                 /* Sanity check: was the requested dim valid */
+                 if (reqdim <= 0 || reqdim > ARR_NDIM(v))
+                         SRF_RETURN_DONE(funcctx);
+ 
+                 /*
+                  * switch to memory context appropriate for multiple function calls
+                  */
+                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+                 fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
+ 
+                 lb = ARR_LBOUND(v);
+                 dimv = ARR_DIMS(v);
+ 
+                 fctx->lower = lb[reqdim - 1];
+                 fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
+                 fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
+ 
+                 funcctx->user_fctx = fctx;
+ 
+                 MemoryContextSwitchTo(oldcontext);
+         }
+ 
+         funcctx = SRF_PERCALL_SETUP();
+ 
+         fctx = funcctx->user_fctx;
+ 
+         if (fctx->lower <= fctx->upper)
+         {
+                 if (!fctx->reverse)
+                         SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
+                 else
+                         SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
+         }
+         else
+                 /* do when there is no more left */
+                 SRF_RETURN_DONE(funcctx);
+ }
*** ./src/include/catalog/pg_proc.h.orig	2008-03-24 17:39:12.000000000 +0100
--- ./src/include/catalog/pg_proc.h	2008-03-24 18:17:01.000000000 +0100
***************
*** 1015,1020 ****
--- 1015,1025 ----
  DESCR("larger of two");
  DATA(insert OID = 516 (  array_smaller	   PGNSP PGUID 12 1 0 f f t f i 2 2277 "2277 2277" _null_ _null_ _null_ array_smaller - _null_ _null_ ));
  DESCR("smaller of two");
+ DATA(insert OID = 1191 (  generate_subscripts PGNSP PGUID 12 1 1000 f f t t v 3 23 "2277 23 16" _null_ _null_ _null_ array_subscripts_direction - _null_ _null_ ));
+ DESCR("array subscripts generator");
+ DATA(insert OID = 1192 (  generate_subscripts PGNSP PGUID 12 1 1000 f f t t v 2 23 "2277 23" _null_ _null_ _null_ array_subscripts - _null_ _null_ ));
+ DESCR("array subscripts generator");
+ 
  
  DATA(insert OID = 760 (  smgrin			   PGNSP PGUID 12 1 0 f f t f s 1 210 "2275" _null_ _null_ _null_  smgrin - _null_ _null_ ));
  DESCR("I/O");
*** ./src/include/utils/array.h.orig	2008-03-24 17:39:36.000000000 +0100
--- ./src/include/utils/array.h	2008-03-24 18:18:12.000000000 +0100
***************
*** 200,205 ****
--- 200,207 ----
  extern Datum array_upper(PG_FUNCTION_ARGS);
  extern Datum array_larger(PG_FUNCTION_ARGS);
  extern Datum array_smaller(PG_FUNCTION_ARGS);
+ extern Datum array_subscripts(PG_FUNCTION_ARGS);
+ extern Datum array_subscripts_direction(PG_FUNCTION_ARGS);
  
  extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
  		  int arraytyplen, int elmlen, bool elmbyval, char elmalign,
*** ./src/test/regress/expected/arrays.out.orig	2008-03-21 13:17:42.000000000 +0100
--- ./src/test/regress/expected/arrays.out	2008-03-24 17:42:13.000000000 +0100
***************
*** 903,905 ****
--- 903,935 ----
  drop type _comptype;
  drop table comptable;
  drop type comptype;
+ create or replace function array_expand(anyarray) 
+ returns setof anyelement as $$
+ select $1[s] from generate_subscripts($1,1) g(s);
+ $$ language sql immutable;
+ create or replace function array_expand2(anyarray) 
+ returns setof anyelement as $$
+ select $1[s1][s2] from generate_subscripts($1,1) g1(s1),
+                    generate_subscripts($1,2) g2(s2);
+ $$ language sql immutable;
+ select * from array_expand(array[1,2,3]);
+  array_expand 
+ --------------
+             1
+             2
+             3
+ (3 rows)
+ 
+ select * from array_expand2(array[[1,2,3],[4,5,6]]);
+  array_expand2 
+ ---------------
+              1
+              2
+              3
+              4
+              5
+              6
+ (6 rows)
+ 
+ drop function array_expand(anyarray);
+ drop function array_expand2(anyarray);
*** ./src/test/regress/sql/arrays.sql.orig	2008-03-21 13:17:43.000000000 +0100
--- ./src/test/regress/sql/arrays.sql	2008-03-24 17:42:13.000000000 +0100
***************
*** 340,342 ****
--- 340,359 ----
  drop type _comptype;
  drop table comptable;
  drop type comptype;
+ 
+ create or replace function array_expand(anyarray) 
+ returns setof anyelement as $$
+ select $1[s] from generate_subscripts($1,1) g(s);
+ $$ language sql immutable;
+ 
+ create or replace function array_expand2(anyarray) 
+ returns setof anyelement as $$
+ select $1[s1][s2] from generate_subscripts($1,1) g1(s1),
+                    generate_subscripts($1,2) g2(s2);
+ $$ language sql immutable;
+ 
+ select * from array_expand(array[1,2,3]);
+ select * from array_expand2(array[[1,2,3],[4,5,6]]);
+ 
+ drop function array_expand(anyarray);
+ drop function array_expand2(anyarray);
