summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Momjian2008-07-16 00:48:54 +0000
committerBruce Momjian2008-07-16 00:48:54 +0000
commit105bece010aff2f8a6b3127fc1ffad112c440d25 (patch)
tree647762176d1e24ea27e70c62ab2924800f092812
parentb8409aa07d2633e638d5bc27dd3777dfbbdee138 (diff)
Add array_fill() to create arrays initialized with a value.
Pavel Stehule
-rw-r--r--doc/src/sgml/func.sgml13
-rw-r--r--src/backend/utils/adt/arrayfuncs.c274
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_proc.h6
-rw-r--r--src/include/utils/array.h2
-rw-r--r--src/test/regress/expected/arrays.out58
-rw-r--r--src/test/regress/sql/arrays.sql14
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]);