mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-27 08:39:28 +08:00
Add array_fill() to create arrays initialized with a value.
Pavel Stehule
This commit is contained in:
parent
2fa42cc9ee
commit
2c773296f8
@ -1,4 +1,4 @@
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.440 2008/07/15 18:24:59 momjian Exp $ -->
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.441 2008/07/16 00:48:53 momjian Exp $ -->
|
||||
|
||||
<chapter id="functions">
|
||||
<title>Functions and Operators</title>
|
||||
@ -9371,6 +9371,19 @@ SELECT NULLIF(value, '(none)') ...
|
||||
<entry><literal>array_dims(ARRAY[[1,2,3], [4,5,6]])</literal></entry>
|
||||
<entry><literal>[1:2][1:3]</literal></entry>
|
||||
</row>
|
||||
<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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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_ ));
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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]);
|
||||
|
Loading…
Reference in New Issue
Block a user