1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-29 10:41:53 +03:00

Add array_fill() to create arrays initialized with a value.

Pavel Stehule
This commit is contained in:
Bruce Momjian
2008-07-16 00:48:54 +00:00
parent 2fa42cc9ee
commit 2c773296f8
7 changed files with 371 additions and 8 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.145 2008/05/12 00:00:51 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.146 2008/07/16 00:48:53 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -95,6 +95,11 @@ static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
int *st, int *endp,
int typlen, bool typbyval, char typalign);
static int array_cmp(FunctionCallInfo fcinfo);
static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbv, int nbytes,
Oid elmtype, int dataoffset);
static ArrayType *array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value,
Oid elmtype, bool isnull,
FunctionCallInfo fcinfo);
/*
@ -4314,3 +4319,272 @@ generate_subscripts_nodir(PG_FUNCTION_ARGS)
/* just call the other one -- it can handle both cases */
return generate_subscripts(fcinfo);
}
/*
* array_fill_with_lower_bounds
* Create and fill array with defined lower bounds.
*/
Datum
array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
{
ArrayType *dims;
ArrayType *lbs;
ArrayType *result;
Oid elmtype;
Datum value;
bool isnull;
if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("dimension array or low bound array cannot be NULL")));
dims = PG_GETARG_ARRAYTYPE_P(1);
lbs = PG_GETARG_ARRAYTYPE_P(2);
if (!PG_ARGISNULL(0))
{
value = PG_GETARG_DATUM(0);
isnull = false;
}
else
{
value = 0;
isnull = true;
}
elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
if (!OidIsValid(elmtype))
elog(ERROR, "could not determine data type of input");
result = array_fill_internal(dims, lbs, value, elmtype, isnull, fcinfo);
PG_RETURN_ARRAYTYPE_P(result);
}
/*
* array_fill
* Create and fill array with default lower bounds.
*/
Datum
array_fill(PG_FUNCTION_ARGS)
{
ArrayType *dims;
ArrayType *result;
Oid elmtype;
Datum value;
bool isnull;
if (PG_ARGISNULL(1))
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("dimension array or low bound array cannot be NULL")));
dims = PG_GETARG_ARRAYTYPE_P(1);
if (!PG_ARGISNULL(0))
{
value = PG_GETARG_DATUM(0);
isnull = false;
}
else
{
value = 0;
isnull = true;
}
elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
if (!OidIsValid(elmtype))
elog(ERROR, "could not determine data type of input");
result = array_fill_internal(dims, NULL, value, elmtype, isnull, fcinfo);
PG_RETURN_ARRAYTYPE_P(result);
}
static ArrayType *
create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
Oid elmtype, int dataoffset)
{
ArrayType *result;
result = (ArrayType *) palloc0(nbytes);
SET_VARSIZE(result, nbytes);
result->ndim = ndims;
result->dataoffset = dataoffset;
result->elemtype = elmtype;
memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
return result;
}
static ArrayType *
array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value,
Oid elmtype, bool isnull,
FunctionCallInfo fcinfo)
{
ArrayType *result;
int *dimv;
int *lbsv;
int ndims;
int nitems;
int deflbs[MAXDIM];
int16 elmlen;
bool elmbyval;
char elmalign;
ArrayMetaState *my_extra;
/*
* Params checks
*/
if (ARR_NDIM(dims) != 1)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong number of array subscripts"),
errhint("Dimension array must be one dimensional.")));
if (ARR_LBOUND(dims)[0] != 1)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong range of array_subscripts"),
errhint("Lower bound of dimension array must be one.")));
if (ARR_HASNULL(dims))
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("dimension values cannot be null")));
dimv = (int *) ARR_DATA_PTR(dims);
ndims = ARR_DIMS(dims)[0];
if (ndims < 0) /* we do allow zero-dimension arrays */
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid number of dimensions: %d", ndims)));
if (ndims > MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
ndims, MAXDIM)));
if (lbs != NULL)
{
if (ARR_NDIM(lbs) != 1)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong number of array subscripts"),
errhint("Dimension array must be one dimensional.")));
if (ARR_LBOUND(lbs)[0] != 1)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong range of array_subscripts"),
errhint("Lower bound of dimension array must be one.")));
if (ARR_HASNULL(lbs))
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("dimension values cannot be null")));
if (ARR_DIMS(lbs)[0] != ndims)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong number of array_subscripts"),
errhint("Low bound array has different size than dimensions array.")));
lbsv = (int *) ARR_DATA_PTR(lbs);
}
else
{
int i;
for (i = 0; i < MAXDIM; i++)
deflbs[i] = 1;
lbsv = deflbs;
}
/* fast track for empty array */
if (ndims == 0)
return construct_empty_array(elmtype);
nitems = ArrayGetNItems(ndims, dimv);
/*
* We arrange to look up info about element type 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 = InvalidOid;
}
if (my_extra->element_type != elmtype)
{
/* Get info about element type */
get_typlenbyvalalign(elmtype,
&my_extra->typlen,
&my_extra->typbyval,
&my_extra->typalign);
my_extra->element_type = elmtype;
}
elmlen = my_extra->typlen;
elmbyval = my_extra->typbyval;
elmalign = my_extra->typalign;
/* compute required space */
if (!isnull)
{
int i;
char *p;
int nbytes;
Datum aux_value = value;
/* make sure data is not toasted */
if (elmlen == -1)
value = PointerGetDatum(PG_DETOAST_DATUM(value));
nbytes = att_addlength_datum(0, elmlen, value);
nbytes = att_align_nominal(nbytes, elmalign);
nbytes *= nitems;
/* check for overflow of total request */
if (!AllocSizeIsValid(nbytes))
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array size exceeds the maximum allowed (%d)",
(int) MaxAllocSize)));
nbytes += ARR_OVERHEAD_NONULLS(ndims);
result = create_array_envelope(ndims, dimv, lbsv, nbytes,
elmtype, 0);
p = ARR_DATA_PTR(result);
for (i = 0; i < nitems; i++)
p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
/* cleaning up detoasted copies of datum */
if (aux_value != value)
pfree((Pointer) value);
}
else
{
int nbytes;
int dataoffset;
bits8 *bitmap;
dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
nbytes = dataoffset;
result = create_array_envelope(ndims, dimv, lbsv, nbytes,
elmtype, dataoffset);
bitmap = ARR_NULLBITMAP(result);
MemSet(bitmap, 0, (nitems + 7) / 8);
}
return result;
}