summaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/array_userfuncs.c
diff options
context:
space:
mode:
authorPavan Deolasee2015-06-04 08:02:02 +0000
committerPavan Deolasee2015-06-04 08:02:02 +0000
commit1e86f652fa5e214a284f9862c67bb976317ac730 (patch)
tree2478baa40ebd47196528ee0a5aa6f6921d8e5bd9 /src/backend/utils/adt/array_userfuncs.c
parent3165b5fde927ff766921270bd56d3236b6c09c21 (diff)
parent4cb7d671fddc8855c8def2de51fb23df1c8ac0af (diff)
Merge remote-tracking branch 'remotes/PGSQL/master' into XL_NEW_MASTER
Conflicts: COPYRIGHT configure configure.in contrib/Makefile doc/bug.template src/backend/access/common/heaptuple.c src/backend/access/common/printtup.c src/backend/access/transam/Makefile src/backend/access/transam/clog.c src/backend/access/transam/twophase.c src/backend/access/transam/varsup.c src/backend/access/transam/xact.c src/backend/access/transam/xlog.c src/backend/bootstrap/bootstrap.c src/backend/catalog/Makefile src/backend/catalog/catalog.c src/backend/catalog/dependency.c src/backend/catalog/genbki.pl src/backend/catalog/namespace.c src/backend/catalog/pg_aggregate.c src/backend/catalog/pg_proc.c src/backend/catalog/storage.c src/backend/commands/aggregatecmds.c src/backend/commands/analyze.c src/backend/commands/comment.c src/backend/commands/copy.c src/backend/commands/dbcommands.c src/backend/commands/event_trigger.c src/backend/commands/explain.c src/backend/commands/indexcmds.c src/backend/commands/portalcmds.c src/backend/commands/schemacmds.c src/backend/commands/sequence.c src/backend/commands/tablecmds.c src/backend/commands/trigger.c src/backend/commands/vacuum.c src/backend/commands/variable.c src/backend/commands/view.c src/backend/executor/execAmi.c src/backend/executor/execCurrent.c src/backend/executor/execMain.c src/backend/executor/execProcnode.c src/backend/executor/execTuples.c src/backend/executor/execUtils.c src/backend/executor/nodeAgg.c src/backend/executor/nodeModifyTable.c src/backend/executor/nodeSubplan.c src/backend/executor/nodeWindowAgg.c src/backend/libpq/hba.c src/backend/nodes/copyfuncs.c src/backend/nodes/equalfuncs.c src/backend/nodes/outfuncs.c src/backend/nodes/readfuncs.c src/backend/optimizer/path/allpaths.c src/backend/optimizer/path/costsize.c src/backend/optimizer/plan/createplan.c src/backend/optimizer/plan/planagg.c src/backend/optimizer/plan/planner.c src/backend/optimizer/plan/setrefs.c src/backend/optimizer/plan/subselect.c src/backend/optimizer/prep/preptlist.c src/backend/optimizer/prep/prepunion.c src/backend/optimizer/util/pathnode.c src/backend/optimizer/util/plancat.c src/backend/parser/analyze.c src/backend/parser/gram.y src/backend/parser/parse_agg.c src/backend/parser/parse_relation.c src/backend/parser/parse_utilcmd.c src/backend/postmaster/autovacuum.c src/backend/postmaster/pgstat.c src/backend/postmaster/postmaster.c src/backend/replication/logical/decode.c src/backend/storage/buffer/bufmgr.c src/backend/storage/ipc/ipci.c src/backend/storage/ipc/procarray.c src/backend/storage/ipc/procsignal.c src/backend/storage/lmgr/lock.c src/backend/storage/lmgr/lwlock.c src/backend/storage/lmgr/proc.c src/backend/tcop/dest.c src/backend/tcop/postgres.c src/backend/tcop/pquery.c src/backend/tcop/utility.c src/backend/utils/adt/arrayfuncs.c src/backend/utils/adt/date.c src/backend/utils/adt/dbsize.c src/backend/utils/adt/pseudotypes.c src/backend/utils/adt/ri_triggers.c src/backend/utils/adt/ruleutils.c src/backend/utils/adt/version.c src/backend/utils/cache/inval.c src/backend/utils/cache/lsyscache.c src/backend/utils/cache/plancache.c src/backend/utils/cache/relcache.c src/backend/utils/init/globals.c src/backend/utils/init/miscinit.c src/backend/utils/init/postinit.c src/backend/utils/misc/guc.c src/backend/utils/mmgr/portalmem.c src/backend/utils/sort/tuplesort.c src/backend/utils/sort/tuplestore.c src/backend/utils/time/combocid.c src/backend/utils/time/snapmgr.c src/bin/Makefile src/bin/initdb/initdb.c src/bin/pg_ctl/pg_ctl.c src/bin/pg_dump/pg_dump.c src/bin/pgbench/pgbench.c src/bin/psql/tab-complete.c src/include/access/htup.h src/include/access/rmgrlist.h src/include/access/transam.h src/include/access/xact.h src/include/catalog/catalog.h src/include/catalog/namespace.h src/include/catalog/pg_aggregate.h src/include/catalog/pg_namespace.h src/include/catalog/pg_proc.h src/include/catalog/pg_type.h src/include/commands/explain.h src/include/commands/sequence.h src/include/commands/vacuum.h src/include/commands/variable.h src/include/executor/execdesc.h src/include/executor/executor.h src/include/executor/tuptable.h src/include/miscadmin.h src/include/nodes/execnodes.h src/include/nodes/nodes.h src/include/nodes/params.h src/include/nodes/parsenodes.h src/include/nodes/plannodes.h src/include/nodes/primnodes.h src/include/nodes/relation.h src/include/optimizer/cost.h src/include/optimizer/pathnode.h src/include/optimizer/planmain.h src/include/parser/analyze.h src/include/parser/parse_agg.h src/include/parser/parse_utilcmd.h src/include/pg_config.h.win32 src/include/pgstat.h src/include/storage/backendid.h src/include/storage/barrier.h src/include/storage/lwlock.h src/include/storage/proc.h src/include/storage/procarray.h src/include/storage/procsignal.h src/include/storage/smgr.h src/include/tcop/dest.h src/include/tcop/pquery.h src/include/utils/builtins.h src/include/utils/guc.h src/include/utils/lsyscache.h src/include/utils/plancache.h src/include/utils/portal.h src/include/utils/rel.h src/include/utils/tuplesort.h src/include/utils/tuplestore.h src/test/regress/expected/aggregates.out src/test/regress/expected/create_index.out src/test/regress/expected/foreign_data.out src/test/regress/expected/join.out src/test/regress/expected/macaddr.out src/test/regress/expected/polygon.out src/test/regress/expected/rangetypes.out src/test/regress/expected/update.out src/test/regress/input/constraints.source src/test/regress/pg_regress.c src/test/regress/serial_schedule src/test/regress/sql/rangetypes.sql
Diffstat (limited to 'src/backend/utils/adt/array_userfuncs.c')
-rw-r--r--src/backend/utils/adt/array_userfuncs.c632
1 files changed, 520 insertions, 112 deletions
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
index 831466dec9..4177d2da17 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -3,7 +3,7 @@
* array_userfuncs.c
* Misc user-visible array support functions
*
- * Copyright (c) 2003-2014, PostgreSQL Global Development Group
+ * Copyright (c) 2003-2015, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/backend/utils/adt/array_userfuncs.c
@@ -12,106 +12,167 @@
*/
#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 Datum array_position_common(FunctionCallInfo fcinfo);
+
+
+/*
+ * fetch_array_arg_replace_nulls
+ *
+ * Fetch an array-valued argument; if it's null, construct an empty array
+ * value of the proper data type. Also cache basic element type information
+ * in fn_extra.
+ */
+static ArrayType *
+fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno)
+{
+ ArrayType *v;
+ Oid element_type;
+ ArrayMetaState *my_extra;
+
+ /* First collect the array value */
+ if (!PG_ARGISNULL(argno))
+ {
+ v = PG_GETARG_ARRAYTYPE_P(argno);
+ element_type = ARR_ELEMTYPE(v);
+ }
+ else
+ {
+ /* We have to look up the array type and element type */
+ Oid arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno);
+
+ if (!OidIsValid(arr_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("could not determine input data type")));
+ element_type = get_element_type(arr_typeid);
+ if (!OidIsValid(element_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("input data type is not an array")));
+
+ v = construct_empty_array(element_type);
+ }
+
+ /* Now cache required info, which might change from call to call */
+ my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+ if (my_extra == NULL)
+ {
+ my_extra = (ArrayMetaState *)
+ MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(ArrayMetaState));
+ my_extra->element_type = InvalidOid;
+ fcinfo->flinfo->fn_extra = my_extra;
+ }
+
+ if (my_extra->element_type != element_type)
+ {
+ get_typlenbyvalalign(element_type,
+ &my_extra->typlen,
+ &my_extra->typbyval,
+ &my_extra->typalign);
+ my_extra->element_type = element_type;
+ }
+
+ return v;
+}
+
/*-----------------------------------------------------------------------------
- * array_push :
- * push an element onto either end of a one-dimensional array
+ * array_append :
+ * push an element onto the end of a one-dimensional array
*----------------------------------------------------------------------------
*/
Datum
-array_push(PG_FUNCTION_ARGS)
+array_append(PG_FUNCTION_ARGS)
{
ArrayType *v;
Datum newelem;
bool isNull;
+ ArrayType *result;
int *dimv,
*lb;
- ArrayType *result;
int indx;
- Oid element_type;
- int16 typlen;
- bool typbyval;
- char typalign;
- Oid arg0_typeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
- Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
- Oid arg0_elemid;
- Oid arg1_elemid;
ArrayMetaState *my_extra;
- if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("could not determine input data types")));
-
- arg0_elemid = get_element_type(arg0_typeid);
- arg1_elemid = get_element_type(arg1_typeid);
+ v = fetch_array_arg_replace_nulls(fcinfo, 0);
+ isNull = PG_ARGISNULL(1);
+ if (isNull)
+ newelem = (Datum) 0;
+ else
+ newelem = PG_GETARG_DATUM(1);
- if (arg0_elemid != InvalidOid)
- {
- if (PG_ARGISNULL(0))
- v = construct_empty_array(arg0_elemid);
- else
- v = PG_GETARG_ARRAYTYPE_P(0);
- isNull = PG_ARGISNULL(1);
- if (isNull)
- newelem = (Datum) 0;
- else
- newelem = PG_GETARG_DATUM(1);
- }
- else if (arg1_elemid != InvalidOid)
+ if (ARR_NDIM(v) == 1)
{
- if (PG_ARGISNULL(1))
- v = construct_empty_array(arg1_elemid);
- else
- v = PG_GETARG_ARRAYTYPE_P(1);
- isNull = PG_ARGISNULL(0);
- if (isNull)
- newelem = (Datum) 0;
- else
- newelem = PG_GETARG_DATUM(0);
+ /* append newelem */
+ int ub;
+
+ lb = ARR_LBOUND(v);
+ dimv = ARR_DIMS(v);
+ ub = dimv[0] + lb[0] - 1;
+ indx = ub + 1;
+
+ /* overflow? */
+ if (indx < ub)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
}
+ else if (ARR_NDIM(v) == 0)
+ indx = 1;
else
- {
- /* Shouldn't get here given proper type checking in parser */
ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("neither input type is an array")));
- PG_RETURN_NULL(); /* keep compiler quiet */
- }
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("argument must be empty or one-dimensional array")));
+
+ /* Perform element insertion */
+ my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+
+ result = array_set(v, 1, &indx, newelem, isNull,
+ -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
- element_type = ARR_ELEMTYPE(v);
+ PG_RETURN_ARRAYTYPE_P(result);
+}
+
+/*-----------------------------------------------------------------------------
+ * array_prepend :
+ * push an element onto the front of a one-dimensional array
+ *----------------------------------------------------------------------------
+ */
+Datum
+array_prepend(PG_FUNCTION_ARGS)
+{
+ ArrayType *v;
+ Datum newelem;
+ bool isNull;
+ ArrayType *result;
+ int *lb;
+ int indx;
+ ArrayMetaState *my_extra;
+
+ isNull = PG_ARGISNULL(0);
+ if (isNull)
+ newelem = (Datum) 0;
+ else
+ newelem = PG_GETARG_DATUM(0);
+ v = fetch_array_arg_replace_nulls(fcinfo, 1);
if (ARR_NDIM(v) == 1)
{
+ /* prepend newelem */
lb = ARR_LBOUND(v);
- dimv = ARR_DIMS(v);
+ indx = lb[0] - 1;
- if (arg0_elemid != InvalidOid)
- {
- /* append newelem */
- int ub = dimv[0] + lb[0] - 1;
-
- indx = ub + 1;
- /* overflow? */
- if (indx < ub)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("integer out of range")));
- }
- else
- {
- /* prepend newelem */
- indx = lb[0] - 1;
- /* overflow? */
- if (indx > lb[0])
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("integer out of range")));
- }
+ /* overflow? */
+ if (indx > lb[0])
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
}
else if (ARR_NDIM(v) == 0)
indx = 1;
@@ -120,39 +181,13 @@ array_push(PG_FUNCTION_ARGS)
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("argument must be empty or one-dimensional array")));
- /*
- * We arrange to look up info about element type only once per series of
- * calls, assuming the element type doesn't change underneath us.
- */
+ /* Perform element insertion */
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 info about element type */
- get_typlenbyvalalign(element_type,
- &my_extra->typlen,
- &my_extra->typbyval,
- &my_extra->typalign);
- my_extra->element_type = element_type;
- }
- typlen = my_extra->typlen;
- typbyval = my_extra->typbyval;
- typalign = my_extra->typalign;
result = array_set(v, 1, &indx, newelem, isNull,
- -1, typlen, typbyval, typalign);
+ -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
- /*
- * Readjust result's LB to match the input's. This does nothing in the
- * append case, but it's the simplest way to implement the prepend case.
- */
+ /* Readjust result's LB to match the input's, as expected for prepend */
if (ARR_NDIM(v) == 1)
ARR_LBOUND(result)[0] = ARR_LBOUND(v)[0];
@@ -471,7 +506,7 @@ create_singleton_array(FunctionCallInfo fcinfo,
/*
- * ARRAY_AGG aggregate function
+ * ARRAY_AGG(anynonarray) aggregate function
*/
Datum
array_agg_transfn(PG_FUNCTION_ARGS)
@@ -486,14 +521,25 @@ array_agg_transfn(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not determine input data type")));
+ /*
+ * Note: we do not need a run-time check about whether arg1_typeid is a
+ * valid array element type, because the parser would have verified that
+ * while resolving the input/result types of this polymorphic aggregate.
+ */
+
if (!AggCheckCallContext(fcinfo, &aggcontext))
{
/* cannot be called directly because of internal-type argument */
elog(ERROR, "array_agg_transfn called in non-aggregate context");
}
- state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+ if (PG_ARGISNULL(0))
+ state = initArrayResult(arg1_typeid, aggcontext, false);
+ else
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
+
state = accumArrayResult(state,
elem,
PG_ARGISNULL(1),
@@ -516,18 +562,13 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
int dims[1];
int lbs[1];
- /*
- * Test for null before Asserting we are in right context. This is to
- * avoid possible Assert failure in 8.4beta installations, where it is
- * possible for users to create NULL constants of type internal.
- */
- if (PG_ARGISNULL(0))
- PG_RETURN_NULL(); /* returns null iff no input values */
-
/* cannot be called directly because of internal-type argument */
Assert(AggCheckCallContext(fcinfo, NULL));
- state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+ if (state == NULL)
+ PG_RETURN_NULL(); /* returns null iff no input values */
dims[0] = state->nelems;
lbs[0] = 1;
@@ -544,3 +585,370 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(result);
}
+
+/*
+ * ARRAY_AGG(anyarray) aggregate function
+ */
+Datum
+array_agg_array_transfn(PG_FUNCTION_ARGS)
+{
+ Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ MemoryContext aggcontext;
+ ArrayBuildStateArr *state;
+
+ if (arg1_typeid == InvalidOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("could not determine input data type")));
+
+ /*
+ * Note: we do not need a run-time check about whether arg1_typeid is a
+ * valid array type, because the parser would have verified that while
+ * resolving the input/result types of this polymorphic aggregate.
+ */
+
+ if (!AggCheckCallContext(fcinfo, &aggcontext))
+ {
+ /* cannot be called directly because of internal-type argument */
+ elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
+ }
+
+
+ if (PG_ARGISNULL(0))
+ state = initArrayResultArr(arg1_typeid, InvalidOid, aggcontext, false);
+ else
+ state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
+
+ state = accumArrayResultArr(state,
+ PG_GETARG_DATUM(1),
+ PG_ARGISNULL(1),
+ arg1_typeid,
+ aggcontext);
+
+ /*
+ * The transition type for array_agg() is declared to be "internal", which
+ * is a pass-by-value type the same size as a pointer. So we can safely
+ * pass the ArrayBuildStateArr pointer through nodeAgg.c's machinations.
+ */
+ PG_RETURN_POINTER(state);
+}
+
+Datum
+array_agg_array_finalfn(PG_FUNCTION_ARGS)
+{
+ Datum result;
+ ArrayBuildStateArr *state;
+
+ /* cannot be called directly because of internal-type argument */
+ Assert(AggCheckCallContext(fcinfo, NULL));
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
+
+ if (state == NULL)
+ PG_RETURN_NULL(); /* returns null iff no input values */
+
+ /*
+ * Make the result. We cannot release the ArrayBuildStateArr because
+ * sometimes aggregate final functions are re-executed. Rather, it is
+ * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
+ * so.
+ */
+ result = makeArrayResultArr(state, CurrentMemoryContext, false);
+
+ PG_RETURN_DATUM(result);
+}
+
+/*-----------------------------------------------------------------------------
+ * array_position, array_position_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_position(PG_FUNCTION_ARGS)
+{
+ return array_position_common(fcinfo);
+}
+
+Datum
+array_position_start(PG_FUNCTION_ARGS)
+{
+ return array_position_common(fcinfo);
+}
+
+/*
+ * array_position_common
+ * Common code for array_position and array_position_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_position_common(FunctionCallInfo fcinfo)
+{
+ ArrayType *array;
+ Oid collation = PG_GET_COLLATION();
+ Oid element_type;
+ Datum searched_element,
+ value;
+ bool isnull;
+ int position,
+ position_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 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;
+ }
+
+ position = (ARR_LBOUND(array))[0] - 1;
+
+ /* figure out where to start */
+ if (PG_NARGS() == 3)
+ {
+ if (PG_ARGISNULL(2))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("initial position should not be NULL")));
+
+ position_min = PG_GETARG_INT32(2);
+ }
+ else
+ position_min = (ARR_LBOUND(array))[0];
+
+ /*
+ * 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))
+ {
+ position++;
+
+ /* skip initial elements if caller requested so */
+ if (position < position_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(position);
+}
+
+/*-----------------------------------------------------------------------------
+ * array_positions :
+ * return an array of positions 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_positions(PG_FUNCTION_ARGS)
+{
+ ArrayType *array;
+ Oid collation = PG_GET_COLLATION();
+ Oid element_type;
+ Datum searched_element,
+ value;
+ bool isnull;
+ int position;
+ 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);
+
+ position = (ARR_LBOUND(array))[0] - 1;
+
+ /*
+ * 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 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 position iff the element matches the given element.
+ */
+ array_iterator = array_create_iterator(array, 0, my_extra);
+ while (array_iterate(array_iterator, &value, &isnull))
+ {
+ position += 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(position), false,
+ INT4OID, CurrentMemoryContext);
+
+ continue;
+ }
+
+ /* not nulls, so run the operator */
+ if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
+ searched_element, value)))
+ astate =
+ accumArrayResult(astate, Int32GetDatum(position), 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));
+}