diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 9bc051583dcea11e06e15b2c6927d5cd2e152d2b..acd2b92918ef78f8084a7dcab7d5123212b15f9e 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,4 +1,4 @@ - + Functions and Operators @@ -9371,6 +9371,19 @@ SELECT NULLIF(value, '(none)') ... array_dims(ARRAY[[1,2,3], [4,5,6]]) [1:2][1:3] + + + + array_fill(anyelement, anyarray, + , anyarray) + + + anyarray + returns an array initialized with supplied value, + dimensions, and lower bounds + array_fill(7, ARRAY[3], ARRAY[2]) + [2:4]={7,7,7} + diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index b3a2ce86579b838dfe85b737a08c277845dc641c..6c810025e5ef5e1197224d0c25b7abadc8bd105b 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -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; +} diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 247833801c7faa2addda55f6c319b0a9b7e3335c..07f66e80a7f464068691a4b8e14a348db9954cbe 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.467 2008/07/14 00:51:45 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.468 2008/07/16 00:48:53 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200807131 +#define CATALOG_VERSION_NO 200807151 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 310f571290c82936449683b0b2ed5396e8a116aa..63f1cc10d2ef14dc76d73259810ede31efa31572 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.505 2008/07/14 00:51:45 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.506 2008/07/16 00:48:53 momjian Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -1010,8 +1010,10 @@ DATA(insert OID = 1191 ( generate_subscripts PGNSP PGUID 12 1 1000 f f t t i 3 DESCR("array subscripts generator"); DATA(insert OID = 1192 ( generate_subscripts PGNSP PGUID 12 1 1000 f f t t i 2 23 "2277 23" _null_ _null_ _null_ generate_subscripts_nodir - _null_ _null_ )); DESCR("array subscripts generator"); - - +DATA(insert OID = 1193 ( array_fill PGNSP PGUID 12 1 0 f f f f i 2 2277 "2283 1007" _null_ _null_ _null_ array_fill - _null_ _null_ )); +DESCR("array constructor with value"); +DATA(insert OID = 1286 ( array_fill PGNSP PGUID 12 1 0 f f f f i 3 2277 "2283 1007 1007" _null_ _null_ _null_ array_fill_with_lower_bounds - _null_ _null_ )); +DESCR("array constructor with value"); DATA(insert OID = 760 ( smgrin PGNSP PGUID 12 1 0 f f t f s 1 210 "2275" _null_ _null_ _null_ smgrin - _null_ _null_ )); DESCR("I/O"); DATA(insert OID = 761 ( smgrout PGNSP PGUID 12 1 0 f f t f s 1 2275 "210" _null_ _null_ _null_ smgrout - _null_ _null_ )); diff --git a/src/include/utils/array.h b/src/include/utils/array.h index f8595d908b10fa8aadfa7c526d624813caed15ae..9efa78e6f3ef6b560189fdbf9e1310b7efa8315e 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -49,7 +49,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.67 2008/04/28 14:48:57 alvherre Exp $ + * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.68 2008/07/16 00:48:54 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -202,6 +202,8 @@ extern Datum array_larger(PG_FUNCTION_ARGS); extern Datum array_smaller(PG_FUNCTION_ARGS); extern Datum generate_subscripts(PG_FUNCTION_ARGS); extern Datum generate_subscripts_nodir(PG_FUNCTION_ARGS); +extern Datum array_fill(PG_FUNCTION_ARGS); +extern Datum array_fill_with_lower_bounds(PG_FUNCTION_ARGS); extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out index 9ab372d15a591f617f5c574b7e0488b4d7fd6ba0..7b7a01694acecf91bea64887e59803b7a7a481f7 100644 --- a/src/test/regress/expected/arrays.out +++ b/src/test/regress/expected/arrays.out @@ -933,3 +933,61 @@ select * from unnest2(array[[1,2,3],[4,5,6]]); drop function unnest1(anyarray); drop function unnest2(anyarray); +select array_fill(null::integer, array[3,3],array[2,2]); + array_fill +----------------------------------------------------------------- + [2:4][2:4]={{NULL,NULL,NULL},{NULL,NULL,NULL},{NULL,NULL,NULL}} +(1 row) + +select array_fill(null::integer, array[3,3]); + array_fill +------------------------------------------------------ + {{NULL,NULL,NULL},{NULL,NULL,NULL},{NULL,NULL,NULL}} +(1 row) + +select array_fill(null::text, array[3,3],array[2,2]); + array_fill +----------------------------------------------------------------- + [2:4][2:4]={{NULL,NULL,NULL},{NULL,NULL,NULL},{NULL,NULL,NULL}} +(1 row) + +select array_fill(null::text, array[3,3]); + array_fill +------------------------------------------------------ + {{NULL,NULL,NULL},{NULL,NULL,NULL},{NULL,NULL,NULL}} +(1 row) + +select array_fill(7, array[3,3],array[2,2]); + array_fill +-------------------------------------- + [2:4][2:4]={{7,7,7},{7,7,7},{7,7,7}} +(1 row) + +select array_fill(7, array[3,3]); + array_fill +--------------------------- + {{7,7,7},{7,7,7},{7,7,7}} +(1 row) + +select array_fill('juhu'::text, array[3,3],array[2,2]); + array_fill +----------------------------------------------------------------- + [2:4][2:4]={{juhu,juhu,juhu},{juhu,juhu,juhu},{juhu,juhu,juhu}} +(1 row) + +select array_fill('juhu'::text, array[3,3]); + array_fill +------------------------------------------------------ + {{juhu,juhu,juhu},{juhu,juhu,juhu},{juhu,juhu,juhu}} +(1 row) + +-- raise exception +select array_fill(1, null, array[2,2]); +ERROR: dimension array or low bound array cannot be NULL +select array_fill(1, array[2,2], null); +ERROR: dimension array or low bound array cannot be NULL +select array_fill(1, array[3,3], array[1,1,1]); +ERROR: wrong number of array_subscripts +HINT: Low bound array has different size than dimensions array. +select array_fill(1, array[1,2,null]); +ERROR: dimension values cannot be null diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql index 6590cad36c498025424f759b2163753ce6f96e46..868ee4afda75c8c4c64e94aa487a42f78081385c 100644 --- a/src/test/regress/sql/arrays.sql +++ b/src/test/regress/sql/arrays.sql @@ -357,3 +357,17 @@ select * from unnest2(array[[1,2,3],[4,5,6]]); drop function unnest1(anyarray); drop function unnest2(anyarray); + +select array_fill(null::integer, array[3,3],array[2,2]); +select array_fill(null::integer, array[3,3]); +select array_fill(null::text, array[3,3],array[2,2]); +select array_fill(null::text, array[3,3]); +select array_fill(7, array[3,3],array[2,2]); +select array_fill(7, array[3,3]); +select array_fill('juhu'::text, array[3,3],array[2,2]); +select array_fill('juhu'::text, array[3,3]); +-- raise exception +select array_fill(1, null, array[2,2]); +select array_fill(1, array[2,2], null); +select array_fill(1, array[3,3], array[1,1,1]); +select array_fill(1, array[1,2,null]);