Hi

I am sending updated version - it allow third optional argument that
specify where searching should to start. With it is possible repeatably
call this function.

Regards

Pavel

2015-01-17 23:43 GMT+01:00 Pavel Stehule <pavel.steh...@gmail.com>:

> Hi
>
> here is a proof concept of array_offset function
>
> possible question:
>
> * used comparation "=" or "IS NOT DISTINCT FROM"
>
> In this initial proof concept I used "IS NOT DISTINCT FROM" operator - but
> my opinion is not strong in this question. Both has some advantages and
> disadvantages.
>
> Regards
>
> Pavel
>
>
> 2015-01-16 19:12 GMT+01:00 Pavel Stehule <pavel.steh...@gmail.com>:
>
>>
>>
>> 2015-01-16 18:37 GMT+01:00 Jim Nasby <jim.na...@bluetreble.com>:
>>
>>> On 1/16/15 11:16 AM, Pavel Stehule wrote:
>>>
>>>>
>>>>
>>>> 2015-01-16 17:57 GMT+01:00 Jim Nasby <jim.na...@bluetreble.com <mailto:
>>>> jim.na...@bluetreble.com>>:
>>>>
>>>>     On 1/16/15 3:39 AM, Pavel Stehule wrote:
>>>>
>>>>         I am proposing a simple function, that returns a position of
>>>> element in array.
>>>>
>>>>
>>>>     Yes please!
>>>>
>>>>         FUNCTION array_position(anyarray, anyelement) RETURNS int
>>>>
>>>>
>>>>     That won't work on a multi-dimensional array. Ideally it needs to
>>>> accept a slice or an element and return the specifier for the slice.
>>>>
>>>>
>>>> It is question, what is a result - probably, there can be a
>>>> multidimensional variant, where result will be a array
>>>>
>>>> array_position([1,2,3],2) --> 2
>>>> array_position([[1,2],[2,3],[3,4]], [2,3]) --> 2 /* 2nd parameter
>>>> should to have N-1 dimension of first parameter */
>>>>
>>>
>>> The problem with that is you can't actually use '2' to get [2,3] back:
>>>
>>> select (array[[1,2,3],[4,5,6],[7,8,9]])[1] IS NULL;
>>>  ?column?
>>> ----------
>>>  t
>>> (1 row)
>>>
>>
>> yes, but when you are searching a array in array you can use a full slice
>> selection:
>>
>> postgres=# select (ARRAY[[1,2],[4,5]])[1][1:2]; -- [1:2] should be a
>> constant every time in this case -- so it should not be returned
>>   array
>> ---------
>>  {{1,2}}
>> (1 row)
>>
>>
>>
>>
>>>
>>> I think the bigger problem here is we need something better than slices
>>> for handling subsets of arrays. Even if the function returned [2:2] it's
>>> still going to behave differently than it will in the non-array case
>>> because you won't be getting the expected number of dimensions back. :(
>>>
>>
>> you cannot to return a slice and I don't propose it, although we can
>> return a range type or array of range type - but still we cannot to use
>> range for a arrays.
>>
>>>
>>>  array_position_md([1,2,3],2) --> [2]
>>>> array_position_md([[1,2],[2,3],[3,4]], 2) --> [2,1]
>>>>
>>>> another question is how to solve more than one occurrence on one value
>>>> - probably two sets of functions - first returns first occurrence of value,
>>>> second returns set of occurrence
>>>>
>>>
>>> Gee, if only way had some way to return multiple elements of
>>> something... ;P
>>>
>>> In other words, I think all of these should actually return an array of
>>> positions. I think it's OK for someone that only cares about the first
>>> instance to just do [1].
>>
>>
>> there can be two functions - "position" - returns first and "positions"
>> returns all as a array
>>
>>
>>>
>>> --
>>> 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
new file mode 100644
index 9ea1068..b3630b4
*** a/doc/src/sgml/array.sgml
--- b/doc/src/sgml/array.sgml
*************** SELECT * FROM sal_emp WHERE pay_by_quart
*** 600,605 ****
--- 600,614 ----
    index, as described in <xref linkend="indexes-types">.
   </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):
+ 
+ <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
new file mode 100644
index 5e7b000..62c9f7f
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** SELECT NULLIF(value, '(none)') ...
*** 11474,11479 ****
--- 11474,11482 ----
      <primary>array_lower</primary>
    </indexterm>
    <indexterm>
+     <primary>array_offset</primary>
+   </indexterm>
+   <indexterm>
      <primary>array_prepend</primary>
    </indexterm>
    <indexterm>
*************** SELECT NULLIF(value, '(none)') ...
*** 11592,11597 ****
--- 11595,11613 ----
         </row>
         <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 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. </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_prepend</function>(<type>anyelement</type>, <type>anyarray</type>)
           </literal>
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
new file mode 100644
index 600646e..2a65806
*** a/src/backend/utils/adt/array_userfuncs.c
--- b/src/backend/utils/adt/array_userfuncs.c
***************
*** 15,20 ****
--- 15,23 ----
  #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
+ #include "utils/typcache.h"
+ 
+ static int array_offset_common(FunctionCallInfo fcinfo);
  
  
  /*-----------------------------------------------------------------------------
*************** array_agg_array_finalfn(PG_FUNCTION_ARGS
*** 612,614 ****
--- 615,759 ----
  
  	PG_RETURN_DATUM(result);
  }
+ 
+ 
+ /*
+  * 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. 
+  * 
+  * Biggest difference against width_array is unsorted input array.
+  */
+ Datum
+ array_offset(PG_FUNCTION_ARGS)
+ {
+ 	PG_RETURN_INT32(array_offset_common(fcinfo));
+ }
+ 
+ 
+ Datum
+ array_offset_start(PG_FUNCTION_ARGS)
+ {
+ 	PG_RETURN_INT32(array_offset_common(fcinfo));
+ }
+ 
+ /*
+  * Common part for functions array_offset and array_offset_startpos
+  */
+ static int
+ array_offset_common(FunctionCallInfo fcinfo)
+ {
+ 	ArrayType	*array;
+ 	Oid		collation = PG_GET_COLLATION();
+ 	Oid			element_type;
+ 	Datum		searched_element = (Datum) 0,
+ 				    value;
+ 	bool		isnull;
+ 	int32		offset = 0,
+ 			    offset_min = 1;
+ 	bool		found = false;
+ 	TypeCacheEntry		*typentry;
+ 	ArrayMetaState		*my_extra;
+ 	bool	null_search;
+ 	ArrayIterator	array_iterator;
+ 
+ 	if (PG_ARGISNULL(0))
+ 		PG_RETURN_NULL();
+ 
+ 	array = PG_GETARG_ARRAYTYPE_P(0);
+ 	element_type = ARR_ELEMTYPE(array);
+ 
+ 	if (PG_ARGISNULL(1))
+ 	{
+ 		if (!array_contains_nulls(array))
+ 			PG_RETURN_NULL();
+ 		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);
+ 	}
+ 
+ 	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 (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)
+ 		PG_RETURN_NULL();
+ 
+ 	PG_RETURN_INT32(offset);
+ }
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
new file mode 100644
index 5591b46..9ab1da0
*** a/src/backend/utils/adt/arrayfuncs.c
--- b/src/backend/utils/adt/arrayfuncs.c
*************** arraycontained(PG_FUNCTION_ARGS)
*** 3945,3951 ****
   * The passed-in array must remain valid for the lifetime of the iterator.
   */
  ArrayIterator
! array_create_iterator(ArrayType *arr, int slice_ndim)
  {
  	ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
  
--- 3945,3951 ----
   * The passed-in array must remain valid for the lifetime of the iterator.
   */
  ArrayIterator
! array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
  {
  	ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
  
*************** array_create_iterator(ArrayType *arr, in
*** 3962,3971 ****
  	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);
  
  	/*
  	 * Remember the slicing parameters.
--- 3962,3981 ----
  	iterator->arr = arr;
  	iterator->nullbitmap = ARR_NULLBITMAP(arr);
  	iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
! 
! 	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
new file mode 100644
index 9edfdb8..b4424e9
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DATA(insert OID = 515 (  array_larger
*** 895,900 ****
--- 895,904 ----
  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 = 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
new file mode 100644
index 694bce7..53bea2e
*** a/src/include/utils/array.h
--- b/src/include/utils/array.h
*************** extern ArrayBuildStateAny *accumArrayRes
*** 314,320 ****
  extern Datum makeArrayResultAny(ArrayBuildStateAny *astate,
  				   MemoryContext rcontext, bool release);
  
! extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim);
  extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull);
  extern void array_free_iterator(ArrayIterator iterator);
  
--- 314,320 ----
  extern Datum makeArrayResultAny(ArrayBuildStateAny *astate,
  				   MemoryContext rcontext, bool release);
  
! 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);
  
*************** extern Datum array_agg_finalfn(PG_FUNCTI
*** 348,353 ****
--- 348,356 ----
  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);
+ 
  /*
   * 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
new file mode 100644
index ae5421f..3d87520
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
*************** exec_stmt_foreach_a(PLpgSQL_execstate *e
*** 2304,2310 ****
  			  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);
  
  	/* Identify iterator result type */
  	if (stmt->slice > 0)
--- 2304,2310 ----
  			  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, 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
new file mode 100644
index d33c9b9..c34f61c
*** a/src/test/regress/expected/arrays.out
--- b/src/test/regress/expected/arrays.out
*************** SELECT array_cat(ARRAY[[3,4],[5,6]], ARR
*** 366,371 ****
--- 366,441 ----
   {{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)
+ 
+ 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
new file mode 100644
index 733c19b..68169b2
*** a/src/test/regress/sql/arrays.sql
--- b/src/test/regress/sql/arrays.sql
*************** SELECT array_cat(ARRAY[1,2], ARRAY[3,4])
*** 185,190 ****
--- 185,214 ----
  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');
+ 
+ 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";
-- 
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