diff options
author | Bruce Momjian | 2008-07-16 00:48:54 +0000 |
---|---|---|
committer | Bruce Momjian | 2008-07-16 00:48:54 +0000 |
commit | 105bece010aff2f8a6b3127fc1ffad112c440d25 (patch) | |
tree | 647762176d1e24ea27e70c62ab2924800f092812 | |
parent | b8409aa07d2633e638d5bc27dd3777dfbbdee138 (diff) |
Add array_fill() to create arrays initialized with a value.
Pavel Stehule
-rw-r--r-- | doc/src/sgml/func.sgml | 13 | ||||
-rw-r--r-- | src/backend/utils/adt/arrayfuncs.c | 274 | ||||
-rw-r--r-- | src/include/catalog/catversion.h | 2 | ||||
-rw-r--r-- | src/include/catalog/pg_proc.h | 6 | ||||
-rw-r--r-- | src/include/utils/array.h | 2 | ||||
-rw-r--r-- | src/test/regress/expected/arrays.out | 58 | ||||
-rw-r--r-- | src/test/regress/sql/arrays.sql | 14 |
7 files changed, 366 insertions, 3 deletions
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 3b178ec157..12cb49642a 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -9374,6 +9374,19 @@ SELECT NULLIF(value, '(none)') ... <row> <entry> <literal> + <function>array_fill</function>(<type>anyelement</type>, <type>anyarray</type>, + <optional>, <type>anyarray</type></optional>) + </literal> + </entry> + <entry><type>anyarray</type></entry> + <entry>returns an array initialized with supplied value, + dimensions, and lower bounds</entry> + <entry><literal>array_fill(7, ARRAY[3], ARRAY[2])</literal></entry> + <entry><literal>[2:4]={7,7,7}</literal></entry> + </row> + <row> + <entry> + <literal> <function>array_lower</function>(<type>anyarray</type>, <type>int</type>) </literal> </entry> diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 57eca82d3c..ea68c138e7 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -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 7df95a46b6..9f68ee01d6 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -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 4d3ce7a6ce..4ee41bd05e 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -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 0bbe410e42..05323f083c 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -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 9ab372d15a..7b7a01694a 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 6590cad36c..868ee4afda 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]); |