array_offset() and array_offsets()
These functions return the offset position or positions of a value in an array. Author: Pavel Stěhule Reviewed by: Jim Nasby
This commit is contained in:
parent
f9dead5624
commit
13dbc7a824
@ -600,6 +600,25 @@ SELECT * FROM sal_emp WHERE pay_by_quarter && ARRAY[10000];
|
|||||||
index, as described in <xref linkend="indexes-types">.
|
index, as described in <xref linkend="indexes-types">.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
You can also search for specific values in an array using the <function>array_offset</>
|
||||||
|
and <function>array_offsets</> functions. The former returns the position of
|
||||||
|
the first occurrence of a value in an array; the latter returns an array with the
|
||||||
|
positions of all occurrences of the value in the array. For example:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
SELECT array_offset(ARRAY['sun','mon','tue','wed','thu','fri','sat'], 'mon');
|
||||||
|
array_offset
|
||||||
|
--------------
|
||||||
|
2
|
||||||
|
|
||||||
|
SELECT array_offsets(ARRAY[1, 4, 3, 1, 3, 4, 2, 1], 1);
|
||||||
|
array_offsets
|
||||||
|
---------------
|
||||||
|
{1,4,8}
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
|
||||||
<tip>
|
<tip>
|
||||||
<para>
|
<para>
|
||||||
Arrays are not sets; searching for specific array elements
|
Arrays are not sets; searching for specific array elements
|
||||||
|
@ -11479,6 +11479,12 @@ SELECT NULLIF(value, '(none)') ...
|
|||||||
<indexterm>
|
<indexterm>
|
||||||
<primary>array_lower</primary>
|
<primary>array_lower</primary>
|
||||||
</indexterm>
|
</indexterm>
|
||||||
|
<indexterm>
|
||||||
|
<primary>array_offset</primary>
|
||||||
|
</indexterm>
|
||||||
|
<indexterm>
|
||||||
|
<primary>array_offsets</primary>
|
||||||
|
</indexterm>
|
||||||
<indexterm>
|
<indexterm>
|
||||||
<primary>array_prepend</primary>
|
<primary>array_prepend</primary>
|
||||||
</indexterm>
|
</indexterm>
|
||||||
@ -11596,6 +11602,32 @@ SELECT NULLIF(value, '(none)') ...
|
|||||||
<entry><literal>array_lower('[0:2]={1,2,3}'::int[], 1)</literal></entry>
|
<entry><literal>array_lower('[0:2]={1,2,3}'::int[], 1)</literal></entry>
|
||||||
<entry><literal>0</literal></entry>
|
<entry><literal>0</literal></entry>
|
||||||
</row>
|
</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 the offset of the first occurrence of the second
|
||||||
|
argument in the array, starting at the element indicated by the third
|
||||||
|
argument or at the first element (array must be one-dimensional)</entry>
|
||||||
|
<entry><literal>array_offset(ARRAY['sun','mon','tue','wed','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 the second
|
||||||
|
argument in the array given as first argument (array must be
|
||||||
|
one-dimensional)</entry>
|
||||||
|
<entry><literal>array_offsets(ARRAY['A','A','B','A'], 'A')</literal></entry>
|
||||||
|
<entry><literal>{1,2,4}</literal></entry>
|
||||||
|
</row>
|
||||||
<row>
|
<row>
|
||||||
<entry>
|
<entry>
|
||||||
<literal>
|
<literal>
|
||||||
@ -11707,6 +11739,23 @@ NULL baz</literallayout>(3 rows)</entry>
|
|||||||
</tgroup>
|
</tgroup>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
In <function>array_offset</function> and <function>array_offsets</>,
|
||||||
|
each array element is compared to the searched value using
|
||||||
|
<literal>IS NOT DISTINCT FROM</literal> semantics.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
In <function>array_offset</function>, <literal>NULL</literal> is returned
|
||||||
|
if the value is not found.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
In <function>array_offsets</function>, <literal>NULL</literal> is returned
|
||||||
|
only if the array is <literal>NULL</literal>; if the value is not found in
|
||||||
|
the array, an empty array is returned instead.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
In <function>string_to_array</function>, if the delimiter parameter is
|
In <function>string_to_array</function>, if the delimiter parameter is
|
||||||
NULL, each character in the input string will become a separate element in
|
NULL, each character in the input string will become a separate element in
|
||||||
|
@ -12,9 +12,14 @@
|
|||||||
*/
|
*/
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "catalog/pg_type.h"
|
||||||
#include "utils/array.h"
|
#include "utils/array.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/typcache.h"
|
||||||
|
|
||||||
|
|
||||||
|
static Datum array_offset_common(FunctionCallInfo fcinfo);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -652,3 +657,294 @@ array_agg_array_finalfn(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
PG_RETURN_DATUM(result);
|
PG_RETURN_DATUM(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------
|
||||||
|
* array_offset, array_offset_start :
|
||||||
|
* return the offset of a value in an array.
|
||||||
|
*
|
||||||
|
* IS NOT DISTINCT FROM semantics are used for comparisons. Return NULL when
|
||||||
|
* the value is not found.
|
||||||
|
*-----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
array_offset(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
return array_offset_common(fcinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
array_offset_start(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
return array_offset_common(fcinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* array_offset_common
|
||||||
|
* Common code for array_offset and array_offset_start
|
||||||
|
*
|
||||||
|
* These are separate wrappers for the sake of opr_sanity regression test.
|
||||||
|
* They are not strict so we have to test for null inputs explicitly.
|
||||||
|
*/
|
||||||
|
static Datum
|
||||||
|
array_offset_common(FunctionCallInfo fcinfo)
|
||||||
|
{
|
||||||
|
ArrayType *array;
|
||||||
|
Oid collation = PG_GET_COLLATION();
|
||||||
|
Oid element_type;
|
||||||
|
Datum searched_element,
|
||||||
|
value;
|
||||||
|
bool isnull;
|
||||||
|
int offset = 0,
|
||||||
|
offset_min;
|
||||||
|
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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We refuse to search for elements in multi-dimensional arrays, since we
|
||||||
|
* have no good way to report the element's location in the array.
|
||||||
|
*/
|
||||||
|
if (ARR_NDIM(array) > 1)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("searching for elements in multidimensional arrays is not supported")));
|
||||||
|
|
||||||
|
if (PG_ARGISNULL(1))
|
||||||
|
{
|
||||||
|
/* fast return when the array doesn't have have nulls */
|
||||||
|
if (!array_contains_nulls(array))
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
searched_element = (Datum) 0;
|
||||||
|
null_search = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
searched_element = PG_GETARG_DATUM(1);
|
||||||
|
null_search = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* figure out where to start */
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
offset_min = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We arrange to look up type info for array_create_iterator only once per
|
||||||
|
* series of calls, assuming the element type doesn't change underneath us.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Examine each array element until we find a match. */
|
||||||
|
array_iterator = array_create_iterator(array, 0, my_extra);
|
||||||
|
while (array_iterate(array_iterator, &value, &isnull))
|
||||||
|
{
|
||||||
|
offset += 1;
|
||||||
|
|
||||||
|
/* skip initial elements if caller requested so */
|
||||||
|
if (offset < offset_min)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Can't look at the array element's value if it's null; but if we
|
||||||
|
* search for null, we have a hit and are done.
|
||||||
|
*/
|
||||||
|
if (isnull || null_search)
|
||||||
|
{
|
||||||
|
if (isnull && null_search)
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* not nulls, so run the operator */
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------
|
||||||
|
* array_offsets :
|
||||||
|
* return an array of offsets of a value in an array.
|
||||||
|
*
|
||||||
|
* IS NOT DISTINCT FROM semantics are used for comparisons. Returns NULL when
|
||||||
|
* the input array is NULL. When the value is not found in the array, returns
|
||||||
|
* an empty array.
|
||||||
|
*
|
||||||
|
* This is not strict so we have to test for null inputs explicitly.
|
||||||
|
*-----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
array_offsets(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
ArrayType *array;
|
||||||
|
Oid collation = PG_GET_COLLATION();
|
||||||
|
Oid element_type;
|
||||||
|
Datum searched_element,
|
||||||
|
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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We refuse to search for elements in multi-dimensional arrays, since we
|
||||||
|
* have no good way to report the element's location in the array.
|
||||||
|
*/
|
||||||
|
if (ARR_NDIM(array) > 1)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("searching for elements in multidimensional arrays is not supported")));
|
||||||
|
|
||||||
|
astate = initArrayResult(INT4OID, CurrentMemoryContext, false);
|
||||||
|
|
||||||
|
if (PG_ARGISNULL(1))
|
||||||
|
{
|
||||||
|
/* fast return when the array doesn't have have nulls */
|
||||||
|
if (!array_contains_nulls(array))
|
||||||
|
PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
|
||||||
|
searched_element = (Datum) 0;
|
||||||
|
null_search = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
searched_element = PG_GETARG_DATUM(1);
|
||||||
|
null_search = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We arrange to look up type info for array_create_iterator only once per
|
||||||
|
* series of calls, assuming the element type doesn't change underneath us.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Accumulate each array offset iff the element matches the given element.
|
||||||
|
*/
|
||||||
|
array_iterator = array_create_iterator(array, 0, my_extra);
|
||||||
|
while (array_iterate(array_iterator, &value, &isnull))
|
||||||
|
{
|
||||||
|
offset += 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Can't look at the array element's value if it's null; but if we
|
||||||
|
* search for null, we have a hit.
|
||||||
|
*/
|
||||||
|
if (isnull || null_search)
|
||||||
|
{
|
||||||
|
if (isnull && null_search)
|
||||||
|
astate =
|
||||||
|
accumArrayResult(astate, Int32GetDatum(offset), false,
|
||||||
|
INT4OID, CurrentMemoryContext);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* not nulls, so run the operator */
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
@ -3989,7 +3989,7 @@ arraycontained(PG_FUNCTION_ARGS)
|
|||||||
* The passed-in array must remain valid for the lifetime of the iterator.
|
* The passed-in array must remain valid for the lifetime of the iterator.
|
||||||
*/
|
*/
|
||||||
ArrayIterator
|
ArrayIterator
|
||||||
array_create_iterator(ArrayType *arr, int slice_ndim)
|
array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
|
||||||
{
|
{
|
||||||
ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
|
ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
|
||||||
|
|
||||||
@ -4006,6 +4006,16 @@ array_create_iterator(ArrayType *arr, int slice_ndim)
|
|||||||
iterator->arr = arr;
|
iterator->arr = arr;
|
||||||
iterator->nullbitmap = ARR_NULLBITMAP(arr);
|
iterator->nullbitmap = ARR_NULLBITMAP(arr);
|
||||||
iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(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),
|
get_typlenbyvalalign(ARR_ELEMTYPE(arr),
|
||||||
&iterator->typlen,
|
&iterator->typlen,
|
||||||
&iterator->typbyval,
|
&iterator->typbyval,
|
||||||
|
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 201503151
|
#define CATALOG_VERSION_NO 201503181
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -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");
|
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_ ));
|
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");
|
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_ ));
|
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");
|
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_ ));
|
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_ ));
|
||||||
|
@ -323,7 +323,7 @@ extern ArrayBuildStateAny *accumArrayResultAny(ArrayBuildStateAny *astate,
|
|||||||
extern Datum makeArrayResultAny(ArrayBuildStateAny *astate,
|
extern Datum makeArrayResultAny(ArrayBuildStateAny *astate,
|
||||||
MemoryContext rcontext, bool release);
|
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 bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull);
|
||||||
extern void array_free_iterator(ArrayIterator iterator);
|
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_transfn(PG_FUNCTION_ARGS);
|
||||||
extern Datum array_agg_array_finalfn(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
|
* prototypes for functions defined in array_typanalyze.c
|
||||||
*/
|
*/
|
||||||
|
@ -2315,7 +2315,7 @@ exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
|
|||||||
errmsg("FOREACH loop variable must not be of an array type")));
|
errmsg("FOREACH loop variable must not be of an array type")));
|
||||||
|
|
||||||
/* Create an iterator to step through the array */
|
/* 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 */
|
/* Identify iterator result type */
|
||||||
if (stmt->slice > 0)
|
if (stmt->slice > 0)
|
||||||
|
@ -366,6 +366,106 @@ SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
|
|||||||
{{3,4},{5,6},{1,2}}
|
{{3,4},{5,6},{1,2}}
|
||||||
(1 row)
|
(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);
|
||||||
|
ERROR: searching for elements in multidimensional arrays is not supported
|
||||||
|
SELECT array_offset(ARRAY['sun','mon','tue','wed','thu','fri','sat'], 'mon');
|
||||||
|
array_offset
|
||||||
|
--------------
|
||||||
|
2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT array_offset(ARRAY['sun','mon','tue','wed','thu','fri','sat'], 'sat');
|
||||||
|
array_offset
|
||||||
|
--------------
|
||||||
|
7
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT array_offset(ARRAY['sun','mon','tue','wed','thu','fri','sat'], NULL);
|
||||||
|
array_offset
|
||||||
|
--------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT array_offset(ARRAY['sun','mon','tue','wed','thu',NULL,'fri','sat'], NULL);
|
||||||
|
array_offset
|
||||||
|
--------------
|
||||||
|
6
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT array_offset(ARRAY['sun','mon','tue','wed','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]], 4);
|
||||||
|
ERROR: searching for elements in multidimensional arrays is not supported
|
||||||
|
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
|
-- operators
|
||||||
SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
|
SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
|
||||||
a
|
a
|
||||||
|
@ -185,6 +185,39 @@ 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[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_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['sun','mon','tue','wed','thu','fri','sat'], 'mon');
|
||||||
|
SELECT array_offset(ARRAY['sun','mon','tue','wed','thu','fri','sat'], 'sat');
|
||||||
|
SELECT array_offset(ARRAY['sun','mon','tue','wed','thu','fri','sat'], NULL);
|
||||||
|
SELECT array_offset(ARRAY['sun','mon','tue','wed','thu',NULL,'fri','sat'], NULL);
|
||||||
|
SELECT array_offset(ARRAY['sun','mon','tue','wed','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]], 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
|
-- operators
|
||||||
SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
|
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";
|
SELECT NOT ARRAY[1.1,1.2,1.3] = ARRAY[1.1,1.2,1.3] AS "FALSE";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user