Add array_fill() to create arrays initialized with a value.
authorBruce Momjian
Wed, 16 Jul 2008 00:48:54 +0000 (00:48 +0000)
committerBruce Momjian
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 @@
-
+
 
  
   Functions and Operators
@@ -9371,6 +9371,19 @@ SELECT NULLIF(value, '(none)') ...
         array_dims(ARRAY[[1,2,3], [4,5,6]])
         [1:2][1:3]
        
+       
+        
+         
+          array_fill(anyelementanyarray,
+          anyarray)
+         
+        
+        anyarray
+        returns an array initialized with supplied value,
+        dimensions, and lower bounds
+        array_fill(7, ARRAY[3], ARRAY[2])
+        [2:4]={7,7,7}
+       
        
         
          
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]);