Add array_fill() to create arrays initialized with a value.
authorBruce Momjian <bruce@momjian.us>
Wed, 16 Jul 2008 00:48:54 +0000 (00:48 +0000)
committerBruce Momjian <bruce@momjian.us>
Wed, 16 Jul 2008 00:48:54 +0000 (00:48 +0000)
Pavel Stehule

doc/src/sgml/func.sgml
src/backend/utils/adt/arrayfuncs.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/utils/array.h
src/test/regress/expected/arrays.out
src/test/regress/sql/arrays.sql

index 9bc051583dcea11e06e15b2c6927d5cd2e152d2b..acd2b92918ef78f8084a7dcab7d5123212b15f9e 100644 (file)
@@ -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>
index b3a2ce86579b838dfe85b737a08c277845dc641c..6c810025e5ef5e1197224d0c25b7abadc8bd105b 100644 (file)
@@ -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;
+}
index 247833801c7faa2addda55f6c319b0a9b7e3335c..07f66e80a7f464068691a4b8e14a348db9954cbe 100644 (file)
@@ -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
index 310f571290c82936449683b0b2ed5396e8a116aa..63f1cc10d2ef14dc76d73259810ede31efa31572 100644 (file)
@@ -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_ ));
index f8595d908b10fa8aadfa7c526d624813caed15ae..9efa78e6f3ef6b560189fdbf9e1310b7efa8315e 100644 (file)
@@ -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,
index 9ab372d15a591f617f5c574b7e0488b4d7fd6ba0..7b7a01694acecf91bea64887e59803b7a7a481f7 100644 (file)
@@ -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
index 6590cad36c498025424f759b2163753ce6f96e46..868ee4afda75c8c4c64e94aa487a42f78081385c 100644 (file)
@@ -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]);