Re: array_fill function - Mailing list pgsql-patches
From | Bruce Momjian |
---|---|
Subject | Re: array_fill function |
Date | |
Msg-id | 200807160049.m6G0nZd08107@momjian.us Whole thread Raw |
In response to | array_fill function ("Pavel Stehule" <pavel.stehule@gmail.com>) |
List | pgsql-patches |
Patch applied, with minor adjustments in error message wording, with documntation added; committed patch attached. --------------------------------------------------------------------------- Pavel Stehule wrote: > Hello > > Proposal: http://archives.postgresql.org/pgsql-hackers/2008-06/msg00057.php > > I changed name to array_fill and order of arguments. > > postgres=# SELECT array_fill(0, ARRAY[2,3]); > array_fill > ------------------- > {{0,0,0},{0,0,0}} > (1 row) > > postgres=# SELECT array_fill(0, ARRAY[2,3], ARRAY[1,2]); > array_fill > ------------------------------ > [1:2][2:4]={{0,0,0},{0,0,0}} > (1 row) > > postgres=# SELECT array_fill(0, ARRAY[4], ARRAY[2]); > array_fill > ----------------- > [2:5]={0,0,0,0} > (1 row) > > postgres=# SELECT array_fill(NULL::int, ARRAY[4]); > array_fill > ----------------------- > {NULL,NULL,NULL,NULL} > (1 row) > > Regards > Pavel Stehule [ Attachment, skipping... ] > > -- > Sent via pgsql-patches mailing list (pgsql-patches@postgresql.org) > To make changes to your subscription: > http://www.postgresql.org/mailpref/pgsql-patches -- Bruce Momjian <bruce@momjian.us> http://momjian.us EnterpriseDB http://enterprisedb.com + If your life is a hard drive, Christ can be your backup. + Index: doc/src/sgml/func.sgml =================================================================== RCS file: /cvsroot/pgsql/doc/src/sgml/func.sgml,v retrieving revision 1.440 diff -c -c -r1.440 func.sgml *** doc/src/sgml/func.sgml 15 Jul 2008 18:24:59 -0000 1.440 --- doc/src/sgml/func.sgml 16 Jul 2008 00:42:25 -0000 *************** *** 9374,9379 **** --- 9374,9392 ---- <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> Index: src/backend/utils/adt/arrayfuncs.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v retrieving revision 1.145 diff -c -c -r1.145 arrayfuncs.c *** src/backend/utils/adt/arrayfuncs.c 12 May 2008 00:00:51 -0000 1.145 --- src/backend/utils/adt/arrayfuncs.c 16 Jul 2008 00:42:26 -0000 *************** *** 95,100 **** --- 95,105 ---- 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,4316 **** --- 4319,4590 ---- /* 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; + } Index: src/include/catalog/catversion.h =================================================================== RCS file: /cvsroot/pgsql/src/include/catalog/catversion.h,v retrieving revision 1.467 diff -c -c -r1.467 catversion.h *** src/include/catalog/catversion.h 14 Jul 2008 00:51:45 -0000 1.467 --- src/include/catalog/catversion.h 16 Jul 2008 00:42:27 -0000 *************** *** 53,58 **** */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 200807131 #endif --- 53,58 ---- */ /* yyyymmddN */ ! #define CATALOG_VERSION_NO 200807151 #endif Index: src/include/catalog/pg_proc.h =================================================================== RCS file: /cvsroot/pgsql/src/include/catalog/pg_proc.h,v retrieving revision 1.505 diff -c -c -r1.505 pg_proc.h *** src/include/catalog/pg_proc.h 14 Jul 2008 00:51:45 -0000 1.505 --- src/include/catalog/pg_proc.h 16 Jul 2008 00:42:27 -0000 *************** *** 1010,1017 **** 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 = 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_ )); --- 1010,1019 ---- 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_ )); Index: src/include/utils/array.h =================================================================== RCS file: /cvsroot/pgsql/src/include/utils/array.h,v retrieving revision 1.67 diff -c -c -r1.67 array.h *** src/include/utils/array.h 28 Apr 2008 14:48:57 -0000 1.67 --- src/include/utils/array.h 16 Jul 2008 00:42:28 -0000 *************** *** 202,207 **** --- 202,209 ---- 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, Index: src/test/regress/expected/arrays.out =================================================================== RCS file: /cvsroot/pgsql/src/test/regress/expected/arrays.out,v retrieving revision 1.36 diff -c -c -r1.36 arrays.out *** src/test/regress/expected/arrays.out 28 Apr 2008 14:48:57 -0000 1.36 --- src/test/regress/expected/arrays.out 16 Jul 2008 00:42:28 -0000 *************** *** 933,935 **** --- 933,993 ---- 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 Index: src/test/regress/sql/arrays.sql =================================================================== RCS file: /cvsroot/pgsql/src/test/regress/sql/arrays.sql,v retrieving revision 1.28 diff -c -c -r1.28 arrays.sql *** src/test/regress/sql/arrays.sql 28 Apr 2008 14:48:58 -0000 1.28 --- src/test/regress/sql/arrays.sql 16 Jul 2008 00:42:28 -0000 *************** *** 357,359 **** --- 357,373 ---- 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]);
pgsql-patches by date: