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:

Previous
From: Tatsuo Ishii
Date:
Subject: Re: [HACKERS] WITH RECURSIVE updated to CVS TIP
Next
From: Tom Lane
Date:
Subject: Re: variadic function support