summaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/arrayfuncs.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/arrayfuncs.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/arrayfuncs.c')
-rw-r--r--src/backend/utils/adt/arrayfuncs.c1033
1 files changed, 911 insertions, 122 deletions
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index bf65d2cf43..036b3eefd5 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -8,7 +8,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Portions Copyright (c) 2012-2014, TransLattice, Inc.
- * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
@@ -20,8 +20,13 @@
#include "postgres.h"
#include <ctype.h>
+#ifdef _MSC_VER
+#include <float.h> /* for _isnan */
+#endif
+#include <math.h>
#include "access/htup_details.h"
+#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
#include "utils/array.h"
@@ -135,6 +140,15 @@ static ArrayType *array_replace_internal(ArrayType *array,
Datum replace, bool replace_isnull,
bool remove, Oid collation,
FunctionCallInfo fcinfo);
+static int width_bucket_array_float8(Datum operand, ArrayType *thresholds);
+static int width_bucket_array_fixed(Datum operand,
+ ArrayType *thresholds,
+ Oid collation,
+ TypeCacheEntry *typentry);
+static int width_bucket_array_variable(Datum operand,
+ ArrayType *thresholds,
+ Oid collation,
+ TypeCacheEntry *typentry);
/*
@@ -274,11 +288,13 @@ array_in(PG_FUNCTION_ARGS)
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
ndim + 1, MAXDIM)));
- for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
+ for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++)
+ /* skip */ ;
if (q == p) /* no digits? */
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("missing dimension value")));
+ errmsg("malformed array literal: \"%s\"", string),
+ errdetail("\"[\" must introduce explicitly-specified array dimensions.")));
if (*q == ':')
{
@@ -286,11 +302,13 @@ array_in(PG_FUNCTION_ARGS)
*q = '\0';
lBound[ndim] = atoi(p);
p = q + 1;
- for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
+ for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++)
+ /* skip */ ;
if (q == p) /* no digits? */
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("missing dimension value")));
+ errmsg("malformed array literal: \"%s\"", string),
+ errdetail("Missing array dimension value.")));
}
else
{
@@ -300,7 +318,9 @@ array_in(PG_FUNCTION_ARGS)
if (*q != ']')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("missing \"]\" in array dimensions")));
+ errmsg("malformed array literal: \"%s\"", string),
+ errdetail("Missing \"%s\" after array dimensions.",
+ "]")));
*q = '\0';
ub = atoi(p);
@@ -320,7 +340,8 @@ array_in(PG_FUNCTION_ARGS)
if (*p != '{')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("array value must start with \"{\" or dimension information")));
+ errmsg("malformed array literal: \"%s\"", string),
+ errdetail("Array value must start with \"{\" or dimension information.")));
ndim = ArrayCount(p, dim, typdelim);
for (i = 0; i < ndim; i++)
lBound[i] = 1;
@@ -334,7 +355,9 @@ array_in(PG_FUNCTION_ARGS)
if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("missing assignment operator")));
+ errmsg("malformed array literal: \"%s\"", string),
+ errdetail("Missing \"%s\" after array dimensions.",
+ ASSGN)));
p += strlen(ASSGN);
while (array_isspace(*p))
p++;
@@ -346,18 +369,21 @@ array_in(PG_FUNCTION_ARGS)
if (*p != '{')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("array value must start with \"{\" or dimension information")));
+ errmsg("malformed array literal: \"%s\"", string),
+ errdetail("Array contents must start with \"{\".")));
ndim_braces = ArrayCount(p, dim_braces, typdelim);
if (ndim_braces != ndim)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("array dimensions incompatible with array literal")));
+ errmsg("malformed array literal: \"%s\"", string),
+ errdetail("Specified array dimensions do not match array contents.")));
for (i = 0; i < ndim; ++i)
{
if (dim[i] != dim_braces[i])
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("array dimensions incompatible with array literal")));
+ errmsg("malformed array literal: \"%s\"", string),
+ errdetail("Specified array dimensions do not match array contents.")));
}
}
@@ -487,7 +513,8 @@ ArrayCount(const char *str, int *dim, char typdelim)
/* Signal a premature end of the string */
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("malformed array literal: \"%s\"", str)));
+ errmsg("malformed array literal: \"%s\"", str),
+ errdetail("Unexpected end of input.")));
break;
case '\\':
@@ -502,7 +529,9 @@ ArrayCount(const char *str, int *dim, char typdelim)
parse_state != ARRAY_ELEM_DELIMITED)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("malformed array literal: \"%s\"", str)));
+ errmsg("malformed array literal: \"%s\"", str),
+ errdetail("Unexpected \"%c\" character.",
+ '\\')));
if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
parse_state = ARRAY_ELEM_STARTED;
/* skip the escaped character */
@@ -511,7 +540,8 @@ ArrayCount(const char *str, int *dim, char typdelim)
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("malformed array literal: \"%s\"", str)));
+ errmsg("malformed array literal: \"%s\"", str),
+ errdetail("Unexpected end of input.")));
break;
case '\"':
@@ -525,7 +555,8 @@ ArrayCount(const char *str, int *dim, char typdelim)
parse_state != ARRAY_ELEM_DELIMITED)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("malformed array literal: \"%s\"", str)));
+ errmsg("malformed array literal: \"%s\"", str),
+ errdetail("Unexpected array element.")));
in_quotes = !in_quotes;
if (in_quotes)
parse_state = ARRAY_QUOTED_ELEM_STARTED;
@@ -545,7 +576,9 @@ ArrayCount(const char *str, int *dim, char typdelim)
parse_state != ARRAY_LEVEL_DELIMITED)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("malformed array literal: \"%s\"", str)));
+ errmsg("malformed array literal: \"%s\"", str),
+ errdetail("Unexpected \"%c\" character.",
+ '{')));
parse_state = ARRAY_LEVEL_STARTED;
if (nest_level >= MAXDIM)
ereport(ERROR,
@@ -573,21 +606,25 @@ ArrayCount(const char *str, int *dim, char typdelim)
!(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("malformed array literal: \"%s\"", str)));
+ errmsg("malformed array literal: \"%s\"", str),
+ errdetail("Unexpected \"%c\" character.",
+ '}')));
parse_state = ARRAY_LEVEL_COMPLETED;
if (nest_level == 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("malformed array literal: \"%s\"", str)));
+ errmsg("malformed array literal: \"%s\"", str),
+ errdetail("Unmatched \"%c\" character.", '}')));
nest_level--;
if (nelems_last[nest_level] != 0 &&
nelems[nest_level] != nelems_last[nest_level])
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("multidimensional arrays must have "
- "array expressions with matching "
- "dimensions")));
+ errmsg("malformed array literal: \"%s\"", str),
+ errdetail("Multidimensional arrays must have "
+ "sub-arrays with matching "
+ "dimensions.")));
nelems_last[nest_level] = nelems[nest_level];
nelems[nest_level] = 1;
if (nest_level == 0)
@@ -618,7 +655,9 @@ ArrayCount(const char *str, int *dim, char typdelim)
parse_state != ARRAY_LEVEL_COMPLETED)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("malformed array literal: \"%s\"", str)));
+ errmsg("malformed array literal: \"%s\"", str),
+ errdetail("Unexpected \"%c\" character.",
+ typdelim)));
if (parse_state == ARRAY_LEVEL_COMPLETED)
parse_state = ARRAY_LEVEL_DELIMITED;
else
@@ -639,7 +678,8 @@ ArrayCount(const char *str, int *dim, char typdelim)
parse_state != ARRAY_ELEM_DELIMITED)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("malformed array literal: \"%s\"", str)));
+ errmsg("malformed array literal: \"%s\"", str),
+ errdetail("Unexpected array element.")));
parse_state = ARRAY_ELEM_STARTED;
}
}
@@ -658,7 +698,8 @@ ArrayCount(const char *str, int *dim, char typdelim)
if (!array_isspace(*ptr++))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
- errmsg("malformed array literal: \"%s\"", str)));
+ errmsg("malformed array literal: \"%s\"", str),
+ errdetail("Junk after closing right brace.")));
}
/* special case for an empty array */
@@ -745,7 +786,8 @@ ReadArrayStr(char *arrayStr,
* character.
*
* The error checking in this routine is mostly pro-forma, since we expect
- * that ArrayCount() already validated the string.
+ * that ArrayCount() already validated the string. So we don't bother
+ * with errdetail messages.
*/
srcptr = arrayStr;
while (!eoArray)
@@ -1794,15 +1836,15 @@ array_cardinality(PG_FUNCTION_ARGS)
/*
- * array_ref :
- * This routine takes an array pointer and a subscript array and returns
+ * array_get_element :
+ * This routine takes an array datum and a subscript array and returns
* the referenced item as a Datum. Note that for a pass-by-reference
* datatype, the returned Datum is a pointer into the array object.
*
* This handles both ordinary varlena arrays and fixed-length arrays.
*
* Inputs:
- * array: the array object (mustn't be NULL)
+ * arraydatum: the array object (mustn't be NULL)
* nSubscripts: number of subscripts supplied
* indx[]: the subscript values
* arraytyplen: pg_type.typlen for the array type
@@ -1815,15 +1857,16 @@ array_cardinality(PG_FUNCTION_ARGS)
* *isNull is set to indicate whether the element is NULL.
*/
Datum
-array_ref(ArrayType *array,
- int nSubscripts,
- int *indx,
- int arraytyplen,
- int elmlen,
- bool elmbyval,
- char elmalign,
- bool *isNull)
+array_get_element(Datum arraydatum,
+ int nSubscripts,
+ int *indx,
+ int arraytyplen,
+ int elmlen,
+ bool elmbyval,
+ char elmalign,
+ bool *isNull)
{
+ ArrayType *array;
int i,
ndim,
*dim,
@@ -1845,13 +1888,13 @@ array_ref(ArrayType *array,
fixedLb[0] = 0;
dim = fixedDim;
lb = fixedLb;
- arraydataptr = (char *) array;
+ arraydataptr = (char *) DatumGetPointer(arraydatum);
arraynullsptr = NULL;
}
else
{
/* detoast input array if necessary */
- array = DatumGetArrayTypeP(PointerGetDatum(array));
+ array = DatumGetArrayTypeP(arraydatum);
ndim = ARR_NDIM(array);
dim = ARR_DIMS(array);
@@ -1909,7 +1952,7 @@ array_ref(ArrayType *array,
* This handles both ordinary varlena arrays and fixed-length arrays.
*
* Inputs:
- * array: the array object (mustn't be NULL)
+ * arraydatum: the array object (mustn't be NULL)
* nSubscripts: number of subscripts supplied (must be same for upper/lower)
* upperIndx[]: the upper subscript values
* lowerIndx[]: the lower subscript values
@@ -1924,8 +1967,8 @@ array_ref(ArrayType *array,
* NOTE: we assume it is OK to scribble on the provided subscript arrays
* lowerIndx[] and upperIndx[]. These are generally just temporaries.
*/
-ArrayType *
-array_get_slice(ArrayType *array,
+Datum
+array_get_slice(Datum arraydatum,
int nSubscripts,
int *upperIndx,
int *lowerIndx,
@@ -1934,6 +1977,7 @@ array_get_slice(ArrayType *array,
bool elmbyval,
char elmalign)
{
+ ArrayType *array;
ArrayType *newarray;
int i,
ndim,
@@ -1972,13 +2016,13 @@ array_get_slice(ArrayType *array,
dim = fixedDim;
lb = fixedLb;
elemtype = InvalidOid; /* XXX */
- arraydataptr = (char *) array;
+ arraydataptr = (char *) DatumGetPointer(arraydatum);
arraynullsptr = NULL;
}
else
{
/* detoast input array if necessary */
- array = DatumGetArrayTypeP(PointerGetDatum(array));
+ array = DatumGetArrayTypeP(arraydatum);
ndim = ARR_NDIM(array);
dim = ARR_DIMS(array);
@@ -1994,7 +2038,7 @@ array_get_slice(ArrayType *array,
* slice, return an empty array.
*/
if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
- return construct_empty_array(elemtype);
+ return PointerGetDatum(construct_empty_array(elemtype));
for (i = 0; i < nSubscripts; i++)
{
@@ -2003,7 +2047,7 @@ array_get_slice(ArrayType *array,
if (upperIndx[i] >= (dim[i] + lb[i]))
upperIndx[i] = dim[i] + lb[i] - 1;
if (lowerIndx[i] > upperIndx[i])
- return construct_empty_array(elemtype);
+ return PointerGetDatum(construct_empty_array(elemtype));
}
/* fill any missing subscript positions with full array range */
for (; i < ndim; i++)
@@ -2011,7 +2055,7 @@ array_get_slice(ArrayType *array,
lowerIndx[i] = lb[i];
upperIndx[i] = dim[i] + lb[i] - 1;
if (lowerIndx[i] > upperIndx[i])
- return construct_empty_array(elemtype);
+ return PointerGetDatum(construct_empty_array(elemtype));
}
mda_get_range(ndim, span, lowerIndx, upperIndx);
@@ -2057,18 +2101,18 @@ array_get_slice(ArrayType *array,
lowerIndx, upperIndx,
elmlen, elmbyval, elmalign);
- return newarray;
+ return PointerGetDatum(newarray);
}
/*
- * array_set :
- * This routine sets the value of an array element (specified by
+ * array_set_element :
+ * This routine sets the value of one array element (specified by
* a subscript array) to a new value specified by "dataValue".
*
* This handles both ordinary varlena arrays and fixed-length arrays.
*
* Inputs:
- * array: the initial array object (mustn't be NULL)
+ * arraydatum: the initial array object (mustn't be NULL)
* nSubscripts: number of subscripts supplied
* indx[]: the subscript values
* dataValue: the datum to be inserted at the given position
@@ -2090,17 +2134,18 @@ array_get_slice(ArrayType *array,
* NOTE: For assignments, we throw an error for invalid subscripts etc,
* rather than returning a NULL as the fetch operations do.
*/
-ArrayType *
-array_set(ArrayType *array,
- int nSubscripts,
- int *indx,
- Datum dataValue,
- bool isNull,
- int arraytyplen,
- int elmlen,
- bool elmbyval,
- char elmalign)
+Datum
+array_set_element(Datum arraydatum,
+ int nSubscripts,
+ int *indx,
+ Datum dataValue,
+ bool isNull,
+ int arraytyplen,
+ int elmlen,
+ bool elmbyval,
+ char elmalign)
{
+ ArrayType *array;
ArrayType *newarray;
int i,
ndim,
@@ -2129,6 +2174,8 @@ array_set(ArrayType *array,
* fixed-length arrays -- these are assumed to be 1-d, 0-based. We
* cannot extend them, either.
*/
+ char *resultarray;
+
if (nSubscripts != 1)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
@@ -2144,11 +2191,11 @@ array_set(ArrayType *array,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("cannot assign null value to an element of a fixed-length array")));
- newarray = (ArrayType *) palloc(arraytyplen);
- memcpy(newarray, array, arraytyplen);
- elt_ptr = (char *) newarray + indx[0] * elmlen;
+ resultarray = (char *) palloc(arraytyplen);
+ memcpy(resultarray, DatumGetPointer(arraydatum), arraytyplen);
+ elt_ptr = (char *) resultarray + indx[0] * elmlen;
ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
- return newarray;
+ return PointerGetDatum(resultarray);
}
if (nSubscripts <= 0 || nSubscripts > MAXDIM)
@@ -2161,7 +2208,7 @@ array_set(ArrayType *array,
dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
/* detoast input array if necessary */
- array = DatumGetArrayTypeP(PointerGetDatum(array));
+ array = DatumGetArrayTypeP(arraydatum);
ndim = ARR_NDIM(array);
@@ -2180,9 +2227,10 @@ array_set(ArrayType *array,
lb[i] = indx[i];
}
- return construct_md_array(&dataValue, &isNull, nSubscripts,
- dim, lb, elmtype,
- elmlen, elmbyval, elmalign);
+ return PointerGetDatum(construct_md_array(&dataValue, &isNull,
+ nSubscripts, dim, lb,
+ elmtype,
+ elmlen, elmbyval, elmalign));
}
if (ndim != nSubscripts)
@@ -2344,7 +2392,7 @@ array_set(ArrayType *array,
}
}
- return newarray;
+ return PointerGetDatum(newarray);
}
/*
@@ -2356,12 +2404,12 @@ array_set(ArrayType *array,
* This handles both ordinary varlena arrays and fixed-length arrays.
*
* Inputs:
- * array: the initial array object (mustn't be NULL)
+ * arraydatum: the initial array object (mustn't be NULL)
* nSubscripts: number of subscripts supplied (must be same for upper/lower)
* upperIndx[]: the upper subscript values
* lowerIndx[]: the lower subscript values
- * srcArray: the source for the inserted values
- * isNull: indicates whether srcArray is NULL
+ * srcArrayDatum: the source for the inserted values
+ * isNull: indicates whether srcArrayDatum is NULL
* arraytyplen: pg_type.typlen for the array type
* elmlen: pg_type.typlen for the array's element type
* elmbyval: pg_type.typbyval for the array's element type
@@ -2382,18 +2430,20 @@ array_set(ArrayType *array,
* NOTE: For assignments, we throw an error for silly subscripts etc,
* rather than returning a NULL or empty array as the fetch operations do.
*/
-ArrayType *
-array_set_slice(ArrayType *array,
+Datum
+array_set_slice(Datum arraydatum,
int nSubscripts,
int *upperIndx,
int *lowerIndx,
- ArrayType *srcArray,
+ Datum srcArrayDatum,
bool isNull,
int arraytyplen,
int elmlen,
bool elmbyval,
char elmalign)
{
+ ArrayType *array;
+ ArrayType *srcArray;
ArrayType *newarray;
int i,
ndim,
@@ -2419,7 +2469,7 @@ array_set_slice(ArrayType *array,
/* Currently, assignment from a NULL source array is a no-op */
if (isNull)
- return array;
+ return arraydatum;
if (arraytyplen > 0)
{
@@ -2432,8 +2482,8 @@ array_set_slice(ArrayType *array,
}
/* detoast arrays if necessary */
- array = DatumGetArrayTypeP(PointerGetDatum(array));
- srcArray = DatumGetArrayTypeP(PointerGetDatum(srcArray));
+ array = DatumGetArrayTypeP(arraydatum);
+ srcArray = DatumGetArrayTypeP(srcArrayDatum);
/* note: we assume srcArray contains no toasted elements */
@@ -2466,9 +2516,9 @@ array_set_slice(ArrayType *array,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("source array too small")));
- return construct_md_array(dvalues, dnulls, nSubscripts,
- dim, lb, elmtype,
- elmlen, elmbyval, elmalign);
+ return PointerGetDatum(construct_md_array(dvalues, dnulls, nSubscripts,
+ dim, lb, elmtype,
+ elmlen, elmbyval, elmalign));
}
if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
@@ -2670,7 +2720,43 @@ array_set_slice(ArrayType *array,
}
}
- return newarray;
+ return PointerGetDatum(newarray);
+}
+
+/*
+ * array_ref : backwards compatibility wrapper for array_get_element
+ *
+ * This only works for detoasted/flattened varlena arrays, since the array
+ * argument is declared as "ArrayType *". However there's enough code like
+ * that to justify preserving this API.
+ */
+Datum
+array_ref(ArrayType *array, int nSubscripts, int *indx,
+ int arraytyplen, int elmlen, bool elmbyval, char elmalign,
+ bool *isNull)
+{
+ return array_get_element(PointerGetDatum(array), nSubscripts, indx,
+ arraytyplen, elmlen, elmbyval, elmalign,
+ isNull);
+}
+
+/*
+ * array_set : backwards compatibility wrapper for array_set_element
+ *
+ * This only works for detoasted/flattened varlena arrays, since the array
+ * argument and result are declared as "ArrayType *". However there's enough
+ * code like that to justify preserving this API.
+ */
+ArrayType *
+array_set(ArrayType *array, int nSubscripts, int *indx,
+ Datum dataValue, bool isNull,
+ int arraytyplen, int elmlen, bool elmbyval, char elmalign)
+{
+ return DatumGetArrayTypeP(array_set_element(PointerGetDatum(array),
+ nSubscripts, indx,
+ dataValue, isNull,
+ arraytyplen,
+ elmlen, elmbyval, elmalign));
}
/*
@@ -3944,7 +4030,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));
@@ -3961,10 +4047,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.
@@ -4601,9 +4697,73 @@ array_insert_slice(ArrayType *destArray,
}
/*
+ * initArrayResult - initialize an empty ArrayBuildState
+ *
+ * element_type is the array element type (must be a valid array element type)
+ * rcontext is where to keep working state
+ * subcontext is a flag determining whether to use a separate memory context
+ *
+ * Note: there are two common schemes for using accumArrayResult().
+ * In the older scheme, you start with a NULL ArrayBuildState pointer, and
+ * call accumArrayResult once per element. In this scheme you end up with
+ * a NULL pointer if there were no elements, which you need to special-case.
+ * In the newer scheme, call initArrayResult and then call accumArrayResult
+ * once per element. In this scheme you always end with a non-NULL pointer
+ * that you can pass to makeArrayResult; you get an empty array if there
+ * were no elements. This is preferred if an empty array is what you want.
+ *
+ * It's possible to choose whether to create a separate memory context for the
+ * array build state, or whether to allocate it directly within rcontext.
+ *
+ * When there are many concurrent small states (e.g. array_agg() using hash
+ * aggregation of many small groups), using a separate memory context for each
+ * one may result in severe memory bloat. In such cases, use the same memory
+ * context to initialize all such array build states, and pass
+ * subcontext=false.
+ *
+ * In cases when the array build states have different lifetimes, using a
+ * single memory context is impractical. Instead, pass subcontext=true so that
+ * the array build states can be freed individually.
+ */
+ArrayBuildState *
+initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
+{
+ ArrayBuildState *astate;
+ MemoryContext arr_context = rcontext;
+
+ /* Make a temporary context to hold all the junk */
+ if (subcontext)
+ arr_context = AllocSetContextCreate(rcontext,
+ "accumArrayResult",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+
+ astate = (ArrayBuildState *)
+ MemoryContextAlloc(arr_context, sizeof(ArrayBuildState));
+ astate->mcontext = arr_context;
+ astate->private_cxt = subcontext;
+ astate->alen = (subcontext ? 64 : 8); /* arbitrary starting array size */
+ astate->dvalues = (Datum *)
+ MemoryContextAlloc(arr_context, astate->alen * sizeof(Datum));
+ astate->dnulls = (bool *)
+ MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
+ astate->nelems = 0;
+ astate->element_type = element_type;
+ get_typlenbyvalalign(element_type,
+ &astate->typlen,
+ &astate->typbyval,
+ &astate->typalign);
+
+ return astate;
+}
+
+/*
* accumArrayResult - accumulate one (more) Datum for an array result
*
- * astate is working state (NULL on first call)
+ * astate is working state (can be NULL on first call)
+ * dvalue/disnull represent the new Datum to append to the array
+ * element_type is the Datum's type (must be a valid array element type)
* rcontext is where to keep working state
*/
ArrayBuildState *
@@ -4612,45 +4772,28 @@ accumArrayResult(ArrayBuildState *astate,
Oid element_type,
MemoryContext rcontext)
{
- MemoryContext arr_context,
- oldcontext;
+ MemoryContext oldcontext;
if (astate == NULL)
{
/* First time through --- initialize */
-
- /* Make a temporary context to hold all the junk */
- arr_context = AllocSetContextCreate(rcontext,
- "accumArrayResult",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
- oldcontext = MemoryContextSwitchTo(arr_context);
- astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
- astate->mcontext = arr_context;
- astate->alen = 64; /* arbitrary starting array size */
- astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
- astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
- astate->nelems = 0;
- astate->element_type = element_type;
- get_typlenbyvalalign(element_type,
- &astate->typlen,
- &astate->typbyval,
- &astate->typalign);
+ astate = initArrayResult(element_type, rcontext, true);
}
else
{
- oldcontext = MemoryContextSwitchTo(astate->mcontext);
Assert(astate->element_type == element_type);
- /* enlarge dvalues[]/dnulls[] if needed */
- if (astate->nelems >= astate->alen)
- {
- astate->alen *= 2;
- astate->dvalues = (Datum *)
- repalloc(astate->dvalues, astate->alen * sizeof(Datum));
- astate->dnulls = (bool *)
- repalloc(astate->dnulls, astate->alen * sizeof(bool));
- }
+ }
+
+ oldcontext = MemoryContextSwitchTo(astate->mcontext);
+
+ /* enlarge dvalues[]/dnulls[] if needed */
+ if (astate->nelems >= astate->alen)
+ {
+ astate->alen *= 2;
+ astate->dvalues = (Datum *)
+ repalloc(astate->dvalues, astate->alen * sizeof(Datum));
+ astate->dnulls = (bool *)
+ repalloc(astate->dnulls, astate->alen * sizeof(bool));
}
/*
@@ -4681,20 +4824,27 @@ accumArrayResult(ArrayBuildState *astate,
/*
* makeArrayResult - produce 1-D final result of accumArrayResult
*
- * astate is working state (not NULL)
+ * Note: only releases astate if it was initialized within a separate memory
+ * context (i.e. using subcontext=true when calling initArrayResult).
+ *
+ * astate is working state (must not be NULL)
* rcontext is where to construct result
*/
Datum
makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext)
{
+ int ndims;
int dims[1];
int lbs[1];
+ /* If no elements were presented, we want to create an empty array */
+ ndims = (astate->nelems > 0) ? 1 : 0;
dims[0] = astate->nelems;
lbs[0] = 1;
- return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
+ return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
+ astate->private_cxt);
}
/*
@@ -4703,7 +4853,12 @@ makeArrayResult(ArrayBuildState *astate,
* beware: no check that specified dimensions match the number of values
* accumulated.
*
- * astate is working state (not NULL)
+ * Note: if the astate was not initialized within a separate memory context
+ * (that is, initArrayResult was called with subcontext=false), then using
+ * release=true is illegal. Instead, release astate along with the rest of its
+ * context when appropriate.
+ *
+ * astate is working state (must not be NULL)
* rcontext is where to construct result
* release is true if okay to release working state
*/
@@ -4735,11 +4890,413 @@ makeMdArrayResult(ArrayBuildState *astate,
/* Clean up all the junk */
if (release)
+ {
+ Assert(astate->private_cxt);
+ MemoryContextDelete(astate->mcontext);
+ }
+
+ return PointerGetDatum(result);
+}
+
+/*
+ * The following three functions provide essentially the same API as
+ * initArrayResult/accumArrayResult/makeArrayResult, but instead of accepting
+ * inputs that are array elements, they accept inputs that are arrays and
+ * produce an output array having N+1 dimensions. The inputs must all have
+ * identical dimensionality as well as element type.
+ */
+
+/*
+ * initArrayResultArr - initialize an empty ArrayBuildStateArr
+ *
+ * array_type is the array type (must be a valid varlena array type)
+ * element_type is the type of the array's elements (lookup if InvalidOid)
+ * rcontext is where to keep working state
+ * subcontext is a flag determining whether to use a separate memory context
+ */
+ArrayBuildStateArr *
+initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext,
+ bool subcontext)
+{
+ ArrayBuildStateArr *astate;
+ MemoryContext arr_context = rcontext; /* by default use the parent ctx */
+
+ /* Lookup element type, unless element_type already provided */
+ if (! OidIsValid(element_type))
+ {
+ element_type = get_element_type(array_type);
+
+ if (!OidIsValid(element_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("data type %s is not an array type",
+ format_type_be(array_type))));
+ }
+
+ /* Make a temporary context to hold all the junk */
+ if (subcontext)
+ arr_context = AllocSetContextCreate(rcontext,
+ "accumArrayResultArr",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+
+ /* Note we initialize all fields to zero */
+ astate = (ArrayBuildStateArr *)
+ MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
+ astate->mcontext = arr_context;
+ astate->private_cxt = subcontext;
+
+ /* Save relevant datatype information */
+ astate->array_type = array_type;
+ astate->element_type = element_type;
+
+ return astate;
+}
+
+/*
+ * accumArrayResultArr - accumulate one (more) sub-array for an array result
+ *
+ * astate is working state (can be NULL on first call)
+ * dvalue/disnull represent the new sub-array to append to the array
+ * array_type is the array type (must be a valid varlena array type)
+ * rcontext is where to keep working state
+ */
+ArrayBuildStateArr *
+accumArrayResultArr(ArrayBuildStateArr *astate,
+ Datum dvalue, bool disnull,
+ Oid array_type,
+ MemoryContext rcontext)
+{
+ ArrayType *arg;
+ MemoryContext oldcontext;
+ int *dims,
+ *lbs,
+ ndims,
+ nitems,
+ ndatabytes;
+ char *data;
+ int i;
+
+ /*
+ * We disallow accumulating null subarrays. Another plausible definition
+ * is to ignore them, but callers that want that can just skip calling
+ * this function.
+ */
+ if (disnull)
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("cannot accumulate null arrays")));
+
+ /* Detoast input array in caller's context */
+ arg = DatumGetArrayTypeP(dvalue);
+
+ if (astate == NULL)
+ astate = initArrayResultArr(array_type, InvalidOid, rcontext, true);
+ else
+ Assert(astate->array_type == array_type);
+
+ oldcontext = MemoryContextSwitchTo(astate->mcontext);
+
+ /* Collect this input's dimensions */
+ ndims = ARR_NDIM(arg);
+ dims = ARR_DIMS(arg);
+ lbs = ARR_LBOUND(arg);
+ data = ARR_DATA_PTR(arg);
+ nitems = ArrayGetNItems(ndims, dims);
+ ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
+
+ if (astate->ndims == 0)
+ {
+ /* First input; check/save the dimensionality info */
+
+ /* Should we allow empty inputs and just produce an empty output? */
+ if (ndims == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot accumulate empty arrays")));
+ if (ndims + 1 > MAXDIM)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ ndims + 1, MAXDIM)));
+
+ /*
+ * The output array will have n+1 dimensions, with the ones after the
+ * first matching the input's dimensions.
+ */
+ astate->ndims = ndims + 1;
+ astate->dims[0] = 0;
+ memcpy(&astate->dims[1], dims, ndims * sizeof(int));
+ astate->lbs[0] = 1;
+ memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
+
+ /* Allocate at least enough data space for this item */
+ astate->abytes = 1024;
+ while (astate->abytes <= ndatabytes)
+ astate->abytes *= 2;
+ astate->data = (char *) palloc(astate->abytes);
+ }
+ else
+ {
+ /* Second or later input: must match first input's dimensionality */
+ if (astate->ndims != ndims + 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot accumulate arrays of different dimensionality")));
+ for (i = 0; i < ndims; i++)
+ {
+ if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot accumulate arrays of different dimensionality")));
+ }
+
+ /* Enlarge data space if needed */
+ if (astate->nbytes + ndatabytes > astate->abytes)
+ {
+ astate->abytes = Max(astate->abytes * 2,
+ astate->nbytes + ndatabytes);
+ astate->data = (char *) repalloc(astate->data, astate->abytes);
+ }
+ }
+
+ /*
+ * Copy the data portion of the sub-array. Note we assume that the
+ * advertised data length of the sub-array is properly aligned. We do not
+ * have to worry about detoasting elements since whatever's in the
+ * sub-array should be OK already.
+ */
+ memcpy(astate->data + astate->nbytes, data, ndatabytes);
+ astate->nbytes += ndatabytes;
+
+ /* Deal with null bitmap if needed */
+ if (astate->nullbitmap || ARR_HASNULL(arg))
+ {
+ int newnitems = astate->nitems + nitems;
+
+ if (astate->nullbitmap == NULL)
+ {
+ /*
+ * First input with nulls; we must retrospectively handle any
+ * previous inputs by marking all their items non-null.
+ */
+ astate->aitems = 256;
+ while (astate->aitems <= newnitems)
+ astate->aitems *= 2;
+ astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
+ array_bitmap_copy(astate->nullbitmap, 0,
+ NULL, 0,
+ astate->nitems);
+ }
+ else if (newnitems > astate->aitems)
+ {
+ astate->aitems = Max(astate->aitems * 2, newnitems);
+ astate->nullbitmap = (bits8 *)
+ repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
+ }
+ array_bitmap_copy(astate->nullbitmap, astate->nitems,
+ ARR_NULLBITMAP(arg), 0,
+ nitems);
+ }
+
+ astate->nitems += nitems;
+ astate->dims[0] += 1;
+
+ MemoryContextSwitchTo(oldcontext);
+
+ /* Release detoasted copy if any */
+ if ((Pointer) arg != DatumGetPointer(dvalue))
+ pfree(arg);
+
+ return astate;
+}
+
+/*
+ * makeArrayResultArr - produce N+1-D final result of accumArrayResultArr
+ *
+ * astate is working state (must not be NULL)
+ * rcontext is where to construct result
+ * release is true if okay to release working state
+ */
+Datum
+makeArrayResultArr(ArrayBuildStateArr *astate,
+ MemoryContext rcontext,
+ bool release)
+{
+ ArrayType *result;
+ MemoryContext oldcontext;
+
+ /* Build the final array result in rcontext */
+ oldcontext = MemoryContextSwitchTo(rcontext);
+
+ if (astate->ndims == 0)
+ {
+ /* No inputs, return empty array */
+ result = construct_empty_array(astate->element_type);
+ }
+ else
+ {
+ int dataoffset,
+ nbytes;
+
+ /* Compute required space */
+ nbytes = astate->nbytes;
+ if (astate->nullbitmap != NULL)
+ {
+ dataoffset = ARR_OVERHEAD_WITHNULLS(astate->ndims, astate->nitems);
+ nbytes += dataoffset;
+ }
+ else
+ {
+ dataoffset = 0;
+ nbytes += ARR_OVERHEAD_NONULLS(astate->ndims);
+ }
+
+ result = (ArrayType *) palloc0(nbytes);
+ SET_VARSIZE(result, nbytes);
+ result->ndim = astate->ndims;
+ result->dataoffset = dataoffset;
+ result->elemtype = astate->element_type;
+
+ memcpy(ARR_DIMS(result), astate->dims, astate->ndims * sizeof(int));
+ memcpy(ARR_LBOUND(result), astate->lbs, astate->ndims * sizeof(int));
+ memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
+
+ if (astate->nullbitmap != NULL)
+ array_bitmap_copy(ARR_NULLBITMAP(result), 0,
+ astate->nullbitmap, 0,
+ astate->nitems);
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ /* Clean up all the junk */
+ if (release)
+ {
+ Assert(astate->private_cxt);
MemoryContextDelete(astate->mcontext);
+ }
return PointerGetDatum(result);
}
+/*
+ * The following three functions provide essentially the same API as
+ * initArrayResult/accumArrayResult/makeArrayResult, but can accept either
+ * scalar or array inputs, invoking the appropriate set of functions above.
+ */
+
+/*
+ * initArrayResultAny - initialize an empty ArrayBuildStateAny
+ *
+ * input_type is the input datatype (either element or array type)
+ * rcontext is where to keep working state
+ * subcontext is a flag determining whether to use a separate memory context
+ */
+ArrayBuildStateAny *
+initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
+{
+ ArrayBuildStateAny *astate;
+ Oid element_type = get_element_type(input_type);
+
+ if (OidIsValid(element_type))
+ {
+ /* Array case */
+ ArrayBuildStateArr *arraystate;
+
+ arraystate = initArrayResultArr(input_type, InvalidOid, rcontext, subcontext);
+ astate = (ArrayBuildStateAny *)
+ MemoryContextAlloc(arraystate->mcontext,
+ sizeof(ArrayBuildStateAny));
+ astate->scalarstate = NULL;
+ astate->arraystate = arraystate;
+ }
+ else
+ {
+ /* Scalar case */
+ ArrayBuildState *scalarstate;
+
+ /* Let's just check that we have a type that can be put into arrays */
+ Assert(OidIsValid(get_array_type(input_type)));
+
+ scalarstate = initArrayResult(input_type, rcontext, subcontext);
+ astate = (ArrayBuildStateAny *)
+ MemoryContextAlloc(scalarstate->mcontext,
+ sizeof(ArrayBuildStateAny));
+ astate->scalarstate = scalarstate;
+ astate->arraystate = NULL;
+ }
+
+ return astate;
+}
+
+/*
+ * accumArrayResultAny - accumulate one (more) input for an array result
+ *
+ * astate is working state (can be NULL on first call)
+ * dvalue/disnull represent the new input to append to the array
+ * input_type is the input datatype (either element or array type)
+ * rcontext is where to keep working state
+ */
+ArrayBuildStateAny *
+accumArrayResultAny(ArrayBuildStateAny *astate,
+ Datum dvalue, bool disnull,
+ Oid input_type,
+ MemoryContext rcontext)
+{
+ if (astate == NULL)
+ astate = initArrayResultAny(input_type, rcontext, true);
+
+ if (astate->scalarstate)
+ (void) accumArrayResult(astate->scalarstate,
+ dvalue, disnull,
+ input_type, rcontext);
+ else
+ (void) accumArrayResultArr(astate->arraystate,
+ dvalue, disnull,
+ input_type, rcontext);
+
+ return astate;
+}
+
+/*
+ * makeArrayResultAny - produce final result of accumArrayResultAny
+ *
+ * astate is working state (must not be NULL)
+ * rcontext is where to construct result
+ * release is true if okay to release working state
+ */
+Datum
+makeArrayResultAny(ArrayBuildStateAny *astate,
+ MemoryContext rcontext, bool release)
+{
+ Datum result;
+
+ if (astate->scalarstate)
+ {
+ /* Must use makeMdArrayResult to support "release" parameter */
+ int ndims;
+ int dims[1];
+ int lbs[1];
+
+ /* If no elements were presented, we want to create an empty array */
+ ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
+ dims[0] = astate->scalarstate->nelems;
+ lbs[0] = 1;
+
+ result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
+ rcontext, release);
+ }
+ else
+ {
+ result = makeArrayResultArr(astate->arraystate,
+ rcontext, release);
+ }
+ return result;
+}
+
+
Datum
array_larger(PG_FUNCTION_ARGS)
{
@@ -5543,3 +6100,235 @@ array_replace(PG_FUNCTION_ARGS)
fcinfo);
PG_RETURN_ARRAYTYPE_P(array);
}
+
+/*
+ * Implements width_bucket(anyelement, anyarray).
+ *
+ * 'thresholds' is an array containing lower bound values for each bucket;
+ * these must be sorted from smallest to largest, or bogus results will be
+ * produced. If N thresholds are supplied, the output is from 0 to N:
+ * 0 is for inputs < first threshold, N is for inputs >= last threshold.
+ */
+Datum
+width_bucket_array(PG_FUNCTION_ARGS)
+{
+ Datum operand = PG_GETARG_DATUM(0);
+ ArrayType *thresholds = PG_GETARG_ARRAYTYPE_P(1);
+ Oid collation = PG_GET_COLLATION();
+ Oid element_type = ARR_ELEMTYPE(thresholds);
+ int result;
+
+ /* Check input */
+ if (ARR_NDIM(thresholds) > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("thresholds must be one-dimensional array")));
+
+ if (array_contains_nulls(thresholds))
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("thresholds array must not contain NULLs")));
+
+ /* We have a dedicated implementation for float8 data */
+ if (element_type == FLOAT8OID)
+ result = width_bucket_array_float8(operand, thresholds);
+ else
+ {
+ TypeCacheEntry *typentry;
+
+ /* Cache information about the input type */
+ typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+ if (typentry == NULL ||
+ typentry->type_id != element_type)
+ {
+ typentry = lookup_type_cache(element_type,
+ TYPECACHE_CMP_PROC_FINFO);
+ if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify a comparison function for type %s",
+ format_type_be(element_type))));
+ fcinfo->flinfo->fn_extra = (void *) typentry;
+ }
+
+ /*
+ * We have separate implementation paths for fixed- and variable-width
+ * types, since indexing the array is a lot cheaper in the first case.
+ */
+ if (typentry->typlen > 0)
+ result = width_bucket_array_fixed(operand, thresholds,
+ collation, typentry);
+ else
+ result = width_bucket_array_variable(operand, thresholds,
+ collation, typentry);
+ }
+
+ /* Avoid leaking memory when handed toasted input. */
+ PG_FREE_IF_COPY(thresholds, 1);
+
+ PG_RETURN_INT32(result);
+}
+
+/*
+ * width_bucket_array for float8 data.
+ */
+static int
+width_bucket_array_float8(Datum operand, ArrayType *thresholds)
+{
+ float8 op = DatumGetFloat8(operand);
+ float8 *thresholds_data;
+ int left;
+ int right;
+
+ /*
+ * Since we know the array contains no NULLs, we can just index it
+ * directly.
+ */
+ thresholds_data = (float8 *) ARR_DATA_PTR(thresholds);
+
+ left = 0;
+ right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
+
+ /*
+ * If the probe value is a NaN, it's greater than or equal to all possible
+ * threshold values (including other NaNs), so we need not search. Note
+ * that this would give the same result as searching even if the array
+ * contains multiple NaNs (as long as they're correctly sorted), since the
+ * loop logic will find the rightmost of multiple equal threshold values.
+ */
+ if (isnan(op))
+ return right;
+
+ /* Find the bucket */
+ while (left < right)
+ {
+ int mid = (left + right) / 2;
+
+ if (isnan(thresholds_data[mid]) || op < thresholds_data[mid])
+ right = mid;
+ else
+ left = mid + 1;
+ }
+
+ return left;
+}
+
+/*
+ * width_bucket_array for generic fixed-width data types.
+ */
+static int
+width_bucket_array_fixed(Datum operand,
+ ArrayType *thresholds,
+ Oid collation,
+ TypeCacheEntry *typentry)
+{
+ char *thresholds_data;
+ int typlen = typentry->typlen;
+ bool typbyval = typentry->typbyval;
+ FunctionCallInfoData locfcinfo;
+ int left;
+ int right;
+
+ /*
+ * Since we know the array contains no NULLs, we can just index it
+ * directly.
+ */
+ thresholds_data = (char *) ARR_DATA_PTR(thresholds);
+
+ InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+ collation, NULL, NULL);
+
+ /* Find the bucket */
+ left = 0;
+ right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
+ while (left < right)
+ {
+ int mid = (left + right) / 2;
+ char *ptr;
+ int32 cmpresult;
+
+ ptr = thresholds_data + mid * typlen;
+
+ locfcinfo.arg[0] = operand;
+ locfcinfo.arg[1] = fetch_att(ptr, typbyval, typlen);
+ locfcinfo.argnull[0] = false;
+ locfcinfo.argnull[1] = false;
+ locfcinfo.isnull = false;
+
+ cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+
+ if (cmpresult < 0)
+ right = mid;
+ else
+ left = mid + 1;
+ }
+
+ return left;
+}
+
+/*
+ * width_bucket_array for generic variable-width data types.
+ */
+static int
+width_bucket_array_variable(Datum operand,
+ ArrayType *thresholds,
+ Oid collation,
+ TypeCacheEntry *typentry)
+{
+ char *thresholds_data;
+ int typlen = typentry->typlen;
+ bool typbyval = typentry->typbyval;
+ char typalign = typentry->typalign;
+ FunctionCallInfoData locfcinfo;
+ int left;
+ int right;
+
+ thresholds_data = (char *) ARR_DATA_PTR(thresholds);
+
+ InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
+ collation, NULL, NULL);
+
+ /* Find the bucket */
+ left = 0;
+ right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
+ while (left < right)
+ {
+ int mid = (left + right) / 2;
+ char *ptr;
+ int i;
+ int32 cmpresult;
+
+ /* Locate mid'th array element by advancing from left element */
+ ptr = thresholds_data;
+ for (i = left; i < mid; i++)
+ {
+ ptr = att_addlength_pointer(ptr, typlen, ptr);
+ ptr = (char *) att_align_nominal(ptr, typalign);
+ }
+
+ locfcinfo.arg[0] = operand;
+ locfcinfo.arg[1] = fetch_att(ptr, typbyval, typlen);
+ locfcinfo.argnull[0] = false;
+ locfcinfo.argnull[1] = false;
+ locfcinfo.isnull = false;
+
+ cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
+
+ if (cmpresult < 0)
+ right = mid;
+ else
+ {
+ left = mid + 1;
+
+ /*
+ * Move the thresholds pointer to match new "left" index, so we
+ * don't have to seek over those elements again. This trick
+ * ensures we do only O(N) array indexing work, not O(N^2).
+ */
+ ptr = att_addlength_pointer(ptr, typlen, ptr);
+ thresholds_data = (char *) att_align_nominal(ptr, typalign);
+ }
+ }
+
+ return left;
+}