On 2/22/15 5:19 AM, Pavel Stehule wrote:


2015-02-22 3:00 GMT+01:00 Petr Jelinek <p...@2ndquadrant.com
<mailto:p...@2ndquadrant.com>>:

    On 28/01/15 08:15, Pavel Stehule wrote:



        2015-01-28 0:01 GMT+01:00 Jim Nasby <jim.na...@bluetreble.com
        <mailto:jim.na...@bluetreble.com>
        <mailto:Jim.Nasby@bluetreble.__com
        <mailto:jim.na...@bluetreble.com>>>:

             On 1/27/15 4:36 AM, Pavel Stehule wrote:


                 It is only partially identical - I would to use cache for
                 array_offset, but it is not necessary for array_offsets ..
                 depends how we would to modify current API to support
        externally
                 cached data.


             Externally cached data?


        Some from these functions has own caches for minimize access to
        typcache
        (array_map, array_cmp is example). And in first case, I am trying to
        push these information from fn_extra, in second case I don't do it,
        because I don't expect a repeated call (and I am expecting so
        type cache
        will be enough).


    You actually do caching via fn_extra in both case and I think that's
    the correct way, and yes that part can be moved common function.

    I also see that the documentation does not say what is returned by
    array_offset if nothing is found (it's documented in code but not in
    sgml).


rebased + fixed docs

I don't think we need both array_offset and array_offset_start; can't both SQL functions just call one C function?

It might be worth combining the array and non-array versions of this, by having a _common function that accepts a boolean and then just run one or the other of the while loops. Most of the code seems to be shared between the two versions.

What is this comment supposed to mean? There is no 'width_array'...

 * Biggest difference against width_array is unsorted input array.

I've attached my doc changes, both alone and with the code.
--
Jim Nasby, Data Architect, Blue Treble Consulting
Data in Trouble? Get it in Treble! http://BlueTreble.com
diff --git a/doc/src/sgml/array.sgml b/doc/src/sgml/array.sgml
index 9ea1068..d90266f 100644
--- a/doc/src/sgml/array.sgml
+++ b/doc/src/sgml/array.sgml
@@ -600,6 +600,15 @@ SELECT * FROM sal_emp WHERE pay_by_quarter && ARRAY[10000];
   index, as described in <xref linkend="indexes-types">.
  </para>
 
+ <para>
+  You can also search for a value in an array using the 
<function>array_offset</>
+  function. It returns the position of the first occurrence of a value in an 
array:
+
+<programlisting>
+SELECT array_offset(ARRAY['sun','mon','tue','wen','thu','fri','sat'], 'mon');
+</programlisting>
+ </para>
+
  <tip>
   <para>
    Arrays are not sets; searching for specific array elements
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c198bea..311f2fe 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -11480,6 +11480,9 @@ SELECT NULLIF(value, '(none)') ...
     <primary>array_lower</primary>
   </indexterm>
   <indexterm>
+    <primary>array_offset</primary>
+  </indexterm>
+  <indexterm>
     <primary>array_prepend</primary>
   </indexterm>
   <indexterm>
@@ -11599,6 +11602,37 @@ SELECT NULLIF(value, '(none)') ...
        <row>
         <entry>
          <literal>
+          <function>array_offset</function>(<type>anyarray</type>, 
<type>anyelement</type> <optional>, <type>int</type></optional>)
+         </literal>
+        </entry>
+        <entry><type>int</type></entry>
+        <entry>returns the offset of the first occurrence of a value in an
+        array. It uses the <literal>IS NOT DISTINCT FROM</> operator for
+        comparation. The optional third argument specifies an initial offset to
+        begin the search at.  Returns NULL when the value is not found. Note:
+        multi-dimensional arrays are squashed to one dimension before
+        searching.</entry>
+        
<entry><literal>array_offset(ARRAY['sun','mon','tue','wen','thu','fri','sat'], 
'mon')</literal></entry>
+        <entry><literal>2</literal></entry>
+       </row>
+       <row>
+        <entry>
+         <literal>
+          <function>array_offsets</function>(<type>anyarray</type>, 
<type>anyelement</type>)
+         </literal>
+        </entry>
+        <entry><type>int[]</type></entry>
+        <entry>returns an array of offsets of all occurrences of a value in a 
array. It uses
+        the <literal>IS NOT DISTINCT FROM</> operator for comparation. Returns 
an empty array
+        when there are no occurences of the value in the array. Note:
+        multi-dimensional input arrays are squashed to one dimension before
+        searching.</entry>
+        <entry><literal>array_offsets(ARRAY['A','A','B','A'], 
'A')</literal></entry>
+        <entry><literal>{1,2,4}</literal></entry>
+       </row>
+       <row>
+        <entry>
+         <literal>
           <function>array_prepend</function>(<type>anyelement</type>, 
<type>anyarray</type>)
          </literal>
         </entry>
diff --git a/src/backend/utils/adt/array_userfuncs.c 
b/src/backend/utils/adt/array_userfuncs.c
index 6679333..f7b7932 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -12,9 +12,13 @@
  */
 #include "postgres.h"
 
+#include "catalog/pg_type.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
+#include "utils/typcache.h"
+
+static bool array_offset_common(FunctionCallInfo fcinfo, int *result);
 
 
 /*
@@ -652,3 +656,264 @@ array_agg_array_finalfn(PG_FUNCTION_ARGS)
 
        PG_RETURN_DATUM(result);
 }
+
+
+/*
+ * array_offset - returns the offset of a value in an array.
+ * Returns NULL when value is not found. Uses NOT DISTINCT FROM operator to
+ * allow searching for NULL.
+ * 
+ * Biggest difference against width_array is unsorted input array.
+ */
+Datum
+array_offset(PG_FUNCTION_ARGS)
+{
+       int     result;
+
+       if (array_offset_common(fcinfo, &result))
+               PG_RETURN_INT32(result);
+
+       PG_RETURN_NULL();
+}
+
+
+Datum
+array_offset_start(PG_FUNCTION_ARGS)
+{
+       int     result;
+
+       if (array_offset_common(fcinfo, &result))
+               PG_RETURN_INT32(result);
+
+       PG_RETURN_NULL();
+}
+
+/*
+ * Common part for functions array_offset and array_offset_startpos
+ */
+static bool
+array_offset_common(FunctionCallInfo fcinfo,
+                                                int    *result)
+{
+       ArrayType       *array;
+       Oid             collation = PG_GET_COLLATION();
+       Oid                     element_type;
+       Datum           searched_element = (Datum) 0,
+                                   value;
+       bool            isnull;
+       int             offset = 0,
+                           offset_min = 1;
+       bool            found = false;
+       TypeCacheEntry          *typentry;
+       ArrayMetaState          *my_extra;
+       bool    null_search;
+       ArrayIterator   array_iterator;
+
+       if (PG_ARGISNULL(0))
+               return false;
+
+       array = PG_GETARG_ARRAYTYPE_P(0);
+       element_type = ARR_ELEMTYPE(array);
+
+       if (PG_ARGISNULL(1))
+       {
+               if (!array_contains_nulls(array))
+                       return false;
+               null_search = true;
+       }
+       else
+       {
+               searched_element = PG_GETARG_DATUM(1);
+               null_search = false;
+       }
+
+       if (PG_NARGS() == 3)
+       {
+               if (PG_ARGISNULL(2))
+                       ereport(ERROR,
+                                       
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                                        errmsg("initial offset should not be 
NULL")));
+
+               offset_min = PG_GETARG_INT32(2);
+       }
+
+       /* cache operator info */
+       my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+       if (my_extra == NULL)
+       {
+               fcinfo->flinfo->fn_extra = 
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                                               
                          sizeof(ArrayMetaState));
+               my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+               my_extra->element_type = ~element_type;
+       }
+
+       if (my_extra->element_type != element_type)
+       {
+               get_typlenbyvalalign(element_type,
+                                                &my_extra->typlen,
+                                                &my_extra->typbyval,
+                                                &my_extra->typalign);
+
+               typentry = lookup_type_cache(element_type,
+                                                                        
TYPECACHE_EQ_OPR_FINFO);
+
+               if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                       errmsg("could not identify an equality operator for 
type %s",
+                                  format_type_be(element_type))));
+
+               my_extra->element_type = element_type;
+               fmgr_info(typentry->eq_opr_finfo.fn_oid, &my_extra->proc);
+       }
+
+       array_iterator = array_create_iterator(array, 0, my_extra);
+
+       while (array_iterate(array_iterator, &value, &isnull))
+       {
+               offset += 1;
+
+               if (offset < offset_min)
+                       continue;
+
+               if (isnull || null_search)
+               {
+
+                       if (isnull && null_search)
+                       {
+                               found = true;
+                               break;
+                       }
+                       else
+                               continue;
+               }
+
+               if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
+                                                                               
 searched_element,
+                                                                               
                         value)))
+               {
+                       found = true;
+                       break;
+               }
+       }
+
+       array_free_iterator(array_iterator);
+
+       /* Avoid leaking memory when handed toasted input */
+       PG_FREE_IF_COPY(array, 0);
+
+       if (!found)
+               return false;
+
+       *result = offset;
+
+       return true;
+}
+
+/*
+ * Returns a array of offsets of some value in array.
+ * Returns NULL, the array is NULL. When searching doesn't produce
+ * any value, then it returns empty array.
+ */
+Datum
+array_offsets(PG_FUNCTION_ARGS)
+{
+       ArrayType       *array;
+       Oid             collation = PG_GET_COLLATION();
+       Oid                     element_type;
+       Datum           searched_element = (Datum) 0,
+                                   value;
+       bool            isnull;
+       int             offset = 0;
+       TypeCacheEntry          *typentry;
+       ArrayMetaState          *my_extra;
+       bool    null_search;
+       ArrayIterator   array_iterator;
+       ArrayBuildState *astate = NULL;
+
+       if (PG_ARGISNULL(0))
+               PG_RETURN_NULL();
+
+       array = PG_GETARG_ARRAYTYPE_P(0);
+       element_type = ARR_ELEMTYPE(array);
+
+       astate = initArrayResult(INT4OID, CurrentMemoryContext, false);
+
+       if (PG_ARGISNULL(1))
+       {
+               /* fast return when we have no NULL */
+               if (!array_contains_nulls(array))
+                       PG_RETURN_DATUM(makeArrayResult(astate, 
CurrentMemoryContext));
+
+               null_search = true;
+       }
+       else
+       {
+               searched_element = PG_GETARG_DATUM(1);
+               null_search = false;
+       }
+
+       element_type = ARR_ELEMTYPE(array);
+
+       /* cache operator info */
+       my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+       if (my_extra == NULL)
+       {
+               fcinfo->flinfo->fn_extra = 
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                                               
                          sizeof(ArrayMetaState));
+               my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+               my_extra->element_type = ~element_type;
+       }
+
+       if (my_extra->element_type != element_type)
+       {
+               get_typlenbyvalalign(element_type,
+                                                &my_extra->typlen,
+                                                &my_extra->typbyval,
+                                                &my_extra->typalign);
+
+               typentry = lookup_type_cache(element_type,
+                                                                        
TYPECACHE_EQ_OPR_FINFO);
+
+               if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                       errmsg("could not identify an equality operator for 
type %s",
+                                  format_type_be(element_type))));
+
+               my_extra->element_type = element_type;
+               fmgr_info(typentry->eq_opr_finfo.fn_oid, &my_extra->proc);
+       }
+
+       array_iterator = array_create_iterator(array, 0, my_extra);
+
+       while (array_iterate(array_iterator, &value, &isnull))
+       {
+               offset += 1;
+
+               if (isnull || null_search)
+               {
+
+                       if (isnull && null_search)
+                               astate = accumArrayResult(astate,
+                                                                   
Int32GetDatum(offset), false, INT4OID,
+                                                                   
CurrentMemoryContext);
+
+                       continue;
+               }
+
+               if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
+                                                                               
 searched_element,
+                                                                               
                         value)))
+                       astate = accumArrayResult(astate,
+                                                           
Int32GetDatum(offset), false, INT4OID,
+                                                           
CurrentMemoryContext);
+       }
+
+       array_free_iterator(array_iterator);
+
+       /* Avoid leaking memory when handed toasted input */
+       PG_FREE_IF_COPY(array, 0);
+
+       PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
+}
diff --git a/src/backend/utils/adt/arrayfuncs.c 
b/src/backend/utils/adt/arrayfuncs.c
index 54979fa..9117a55 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3989,7 +3989,7 @@ arraycontained(PG_FUNCTION_ARGS)
  * The passed-in array must remain valid for the lifetime of the iterator.
  */
 ArrayIterator
-array_create_iterator(ArrayType *arr, int slice_ndim)
+array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
 {
        ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
 
@@ -4006,10 +4006,20 @@ array_create_iterator(ArrayType *arr, int slice_ndim)
        iterator->arr = arr;
        iterator->nullbitmap = ARR_NULLBITMAP(arr);
        iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
-       get_typlenbyvalalign(ARR_ELEMTYPE(arr),
-                                                &iterator->typlen,
-                                                &iterator->typbyval,
-                                                &iterator->typalign);
+
+       if (mstate != NULL)
+       {
+               Assert(mstate->element_type == ARR_ELEMTYPE(arr));
+
+               iterator->typlen = mstate->typlen;
+               iterator->typbyval = mstate->typbyval;
+               iterator->typalign = mstate->typalign;
+       }
+       else
+               get_typlenbyvalalign(ARR_ELEMTYPE(arr),
+                                                        &iterator->typlen,
+                                                        &iterator->typbyval,
+                                                        &iterator->typalign);
 
        /*
         * Remember the slicing parameters.
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b8a3660..6a757f3 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -895,6 +895,12 @@ DATA(insert OID = 515 (  array_larger         PGNSP PGUID 
12 1 0 0 0 f f f f t f i 2
 DESCR("larger of two");
 DATA(insert OID = 516 (  array_smaller    PGNSP PGUID 12 1 0 0 0 f f f f t f i 
2 0 2277 "2277 2277" _null_ _null_ _null_ _null_ array_smaller _null_ _null_ 
_null_ ));
 DESCR("smaller of two");
+DATA(insert OID = 3277 (  array_offset            PGNSP PGUID 12 1 0 0 0 f f f 
f f f i 2 0 23 "2277 2283" _null_ _null_ _null_ _null_ array_offset _null_ 
_null_ _null_ ));
+DESCR("returns a offset of value in array");
+DATA(insert OID = 3278 (  array_offset            PGNSP PGUID 12 1 0 0 0 f f f 
f f f i 3 0 23 "2277 2283 23" _null_ _null_ _null_ _null_ array_offset_start 
_null_ _null_ _null_ ));
+DESCR("returns a offset of value in array with start index");
+DATA(insert OID = 3279 (  array_offsets                   PGNSP PGUID 12 1 0 0 
0 f f f f f f i 2 0 1007 "2277 2283" _null_ _null_ _null_ _null_ array_offsets 
_null_ _null_ _null_ ));
+DESCR("returns a array of offsets of some value in array");
 DATA(insert OID = 1191 (  generate_subscripts PGNSP PGUID 12 1 1000 0 0 f f f 
f t t i 3 0 23 "2277 23 16" _null_ _null_ _null_ _null_ generate_subscripts 
_null_ _null_ _null_ ));
 DESCR("array subscripts generator");
 DATA(insert OID = 1192 (  generate_subscripts PGNSP PGUID 12 1 1000 0 0 f f f 
f t t i 2 0 23 "2277 23" _null_ _null_ _null_ _null_ generate_subscripts_nodir 
_null_ _null_ _null_ ));
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 649688c..b78b42a 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -323,7 +323,7 @@ extern ArrayBuildStateAny 
*accumArrayResultAny(ArrayBuildStateAny *astate,
 extern Datum makeArrayResultAny(ArrayBuildStateAny *astate,
                                   MemoryContext rcontext, bool release);
 
-extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim);
+extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim, 
ArrayMetaState *mstate);
 extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull);
 extern void array_free_iterator(ArrayIterator iterator);
 
@@ -358,6 +358,10 @@ extern Datum array_agg_finalfn(PG_FUNCTION_ARGS);
 extern Datum array_agg_array_transfn(PG_FUNCTION_ARGS);
 extern Datum array_agg_array_finalfn(PG_FUNCTION_ARGS);
 
+extern Datum array_offset(PG_FUNCTION_ARGS);
+extern Datum array_offset_start(PG_FUNCTION_ARGS);
+extern Datum array_offsets(PG_FUNCTION_ARGS);
+
 /*
  * prototypes for functions defined in array_typanalyze.c
  */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index f24f55a..c0b4b26 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -2319,7 +2319,7 @@ exec_stmt_foreach_a(PLpgSQL_execstate *estate, 
PLpgSQL_stmt_foreach_a *stmt)
                          errmsg("FOREACH loop variable must not be of an array 
type")));
 
        /* Create an iterator to step through the array */
-       array_iterator = array_create_iterator(arr, stmt->slice);
+       array_iterator = array_create_iterator(arr, stmt->slice, NULL);
 
        /* Identify iterator result type */
        if (stmt->slice > 0)
diff --git a/src/test/regress/expected/arrays.out 
b/src/test/regress/expected/arrays.out
index d33c9b9..75fc16a 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -366,6 +366,113 @@ SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS 
"{{3,4},{5,6},{1,2}}";
  {{3,4},{5,6},{1,2}}
 (1 row)
 
+SELECT array_offset(ARRAY[1,2,3,4,5], 4);
+ array_offset 
+--------------
+            4
+(1 row)
+
+SELECT array_offset(ARRAY[5,3,4,2,1], 4);
+ array_offset 
+--------------
+            3
+(1 row)
+
+SELECT array_offset(ARRAY[[1,2],[3,4]], 3);
+ array_offset 
+--------------
+            3
+(1 row)
+
+SELECT array_offset(ARRAY[[1,2],[3,4]], 4);
+ array_offset 
+--------------
+            4
+(1 row)
+
+SELECT array_offset(ARRAY['sun','mon','tue','wen','thu','fri','sat'], 'mon');
+ array_offset 
+--------------
+            2
+(1 row)
+
+SELECT array_offset(ARRAY['sun','mon','tue','wen','thu','fri','sat'], 'sat');
+ array_offset 
+--------------
+            7
+(1 row)
+
+SELECT array_offset(ARRAY['sun','mon','tue','wen','thu','fri','sat'], NULL);
+ array_offset 
+--------------
+             
+(1 row)
+
+SELECT array_offset(ARRAY['sun','mon','tue','wen','thu',NULL,'fri','sat'], 
NULL);
+ array_offset 
+--------------
+            6
+(1 row)
+
+SELECT array_offset(ARRAY['sun','mon','tue','wen','thu',NULL,'fri','sat'], 
'sat');
+ array_offset 
+--------------
+            8
+(1 row)
+
+SELECT array_offsets(NULL, 10);
+ array_offsets 
+---------------
+ 
+(1 row)
+
+SELECT array_offsets(NULL, NULL::int);
+ array_offsets 
+---------------
+ 
+(1 row)
+
+SELECT array_offsets(ARRAY[1,2,3,4,5,6,1,2,3,4,5,6], 4);
+ array_offsets 
+---------------
+ {4,10}
+(1 row)
+
+SELECT array_offsets(ARRAY[1,2,3,4,5,6,1,2,3,4,5,6], NULL);
+ array_offsets 
+---------------
+ {}
+(1 row)
+
+SELECT array_offsets(ARRAY[1,2,3,NULL,5,6,1,2,3,NULL,5,6], NULL);
+ array_offsets 
+---------------
+ {4,10}
+(1 row)
+
+SELECT array_length(array_offsets(ARRAY(SELECT 
'AAAAAAAAAAAAAAAAAAAAAAAAA'::text || i % 10 FROM generate_series(1,100) g(i)),
+                                  'AAAAAAAAAAAAAAAAAAAAAAAAA5'),1);
+ array_length 
+--------------
+           10
+(1 row)
+
+do $$
+declare 
+  o int;
+  a int[] := ARRAY[1,2,3,2,3,1,2];
+begin
+  o := array_offset(a, 2);
+  while o is not null
+  loop
+    raise notice '%', o;
+    o := array_offset(a, 2, o + 1);
+  end loop;
+end
+$$ language plpgsql;
+NOTICE:  2
+NOTICE:  4
+NOTICE:  7
 -- operators
 SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
        a       
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 733c19b..86058a5 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -185,6 +185,38 @@ SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{1,2,3,4}";
 SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
 SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
 
+SELECT array_offset(ARRAY[1,2,3,4,5], 4);
+SELECT array_offset(ARRAY[5,3,4,2,1], 4);
+SELECT array_offset(ARRAY[[1,2],[3,4]], 3);
+SELECT array_offset(ARRAY[[1,2],[3,4]], 4);
+SELECT array_offset(ARRAY['sun','mon','tue','wen','thu','fri','sat'], 'mon');
+SELECT array_offset(ARRAY['sun','mon','tue','wen','thu','fri','sat'], 'sat');
+SELECT array_offset(ARRAY['sun','mon','tue','wen','thu','fri','sat'], NULL);
+SELECT array_offset(ARRAY['sun','mon','tue','wen','thu',NULL,'fri','sat'], 
NULL);
+SELECT array_offset(ARRAY['sun','mon','tue','wen','thu',NULL,'fri','sat'], 
'sat');
+
+SELECT array_offsets(NULL, 10);
+SELECT array_offsets(NULL, NULL::int);
+SELECT array_offsets(ARRAY[1,2,3,4,5,6,1,2,3,4,5,6], 4);
+SELECT array_offsets(ARRAY[1,2,3,4,5,6,1,2,3,4,5,6], NULL);
+SELECT array_offsets(ARRAY[1,2,3,NULL,5,6,1,2,3,NULL,5,6], NULL);
+SELECT array_length(array_offsets(ARRAY(SELECT 
'AAAAAAAAAAAAAAAAAAAAAAAAA'::text || i % 10 FROM generate_series(1,100) g(i)),
+                                  'AAAAAAAAAAAAAAAAAAAAAAAAA5'),1);
+
+do $$
+declare 
+  o int;
+  a int[] := ARRAY[1,2,3,2,3,1,2];
+begin
+  o := array_offset(a, 2);
+  while o is not null
+  loop
+    raise notice '%', o;
+    o := array_offset(a, 2, o + 1);
+  end loop;
+end
+$$ language plpgsql;
+
 -- operators
 SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
 SELECT NOT ARRAY[1.1,1.2,1.3] = ARRAY[1.1,1.2,1.3] AS "FALSE";
diff --git a/doc/src/sgml/array.sgml b/doc/src/sgml/array.sgml
index b3630b4..d90266f 100644
--- a/doc/src/sgml/array.sgml
+++ b/doc/src/sgml/array.sgml
@@ -601,8 +601,8 @@ SELECT * FROM sal_emp WHERE pay_by_quarter && ARRAY[10000];
  </para>
 
  <para>
-  You can also search any value in array using the <function>array_offset</>
-  function (It returns a position of first occurrence of value in the array):
+  You can also search for a value in an array using the 
<function>array_offset</>
+  function. It returns the position of the first occurrence of a value in an 
array:
 
 <programlisting>
 SELECT array_offset(ARRAY['sun','mon','tue','wen','thu','fri','sat'], 'mon');
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 48b23ee..311f2fe 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -11606,10 +11606,12 @@ SELECT NULLIF(value, '(none)') ...
          </literal>
         </entry>
         <entry><type>int</type></entry>
-        <entry>returns a offset of first occurrence of some element in a 
array. It uses
-        a <literal>IS NOT DISTINCT FROM</> operator for comparation. Third
-        optional argument can specify a initial offset when searching starts.
-        Returns NULL when there are not occurence of value in array.</entry>
+        <entry>returns the offset of the first occurrence of a value in an
+        array. It uses the <literal>IS NOT DISTINCT FROM</> operator for
+        comparation. The optional third argument specifies an initial offset to
+        begin the search at.  Returns NULL when the value is not found. Note:
+        multi-dimensional arrays are squashed to one dimension before
+        searching.</entry>
         
<entry><literal>array_offset(ARRAY['sun','mon','tue','wen','thu','fri','sat'], 
'mon')</literal></entry>
         <entry><literal>2</literal></entry>
        </row>
@@ -11620,9 +11622,11 @@ SELECT NULLIF(value, '(none)') ...
          </literal>
         </entry>
         <entry><type>int[]</type></entry>
-        <entry>returns a array of offset of all occurrences some element in a 
array. It uses
-        a <literal>IS NOT DISTINCT FROM</> operator for comparation. Returns 
empty array,
-        when there are no occurence of element in input array.</entry>
+        <entry>returns an array of offsets of all occurrences of a value in a 
array. It uses
+        the <literal>IS NOT DISTINCT FROM</> operator for comparation. Returns 
an empty array
+        when there are no occurences of the value in the array. Note:
+        multi-dimensional input arrays are squashed to one dimension before
+        searching.</entry>
         <entry><literal>array_offsets(ARRAY['A','A','B','A'], 
'A')</literal></entry>
         <entry><literal>{1,2,4}</literal></entry>
        </row>
diff --git a/src/backend/utils/adt/array_userfuncs.c 
b/src/backend/utils/adt/array_userfuncs.c
index 0b53b9a..f7b7932 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -659,9 +659,9 @@ array_agg_array_finalfn(PG_FUNCTION_ARGS)
 
 
 /*
- * array_offset - returns a offset of entered element in a array.
- * Returns NULL when values is not a element of the array. It allow
- * searching a NULL value due using a NOT DISTINCT FROM operator. 
+ * array_offset - returns the offset of a value in an array.
+ * Returns NULL when value is not found. Uses NOT DISTINCT FROM operator to
+ * allow searching for NULL.
  * 
  * Biggest difference against width_array is unsorted input array.
  */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to