Header And Logo

PostgreSQL
| The world's most advanced open source database.

arrayfuncs.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * arrayfuncs.c
00004  *    Support functions for arrays.
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/utils/adt/arrayfuncs.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 #include "postgres.h"
00016 
00017 #include <ctype.h>
00018 
00019 #include "access/htup_details.h"
00020 #include "funcapi.h"
00021 #include "libpq/pqformat.h"
00022 #include "utils/array.h"
00023 #include "utils/builtins.h"
00024 #include "utils/datum.h"
00025 #include "utils/lsyscache.h"
00026 #include "utils/memutils.h"
00027 #include "utils/typcache.h"
00028 
00029 
00030 /*
00031  * GUC parameter
00032  */
00033 bool        Array_nulls = true;
00034 
00035 /*
00036  * Local definitions
00037  */
00038 #define ASSGN    "="
00039 
00040 typedef enum
00041 {
00042     ARRAY_NO_LEVEL,
00043     ARRAY_LEVEL_STARTED,
00044     ARRAY_ELEM_STARTED,
00045     ARRAY_ELEM_COMPLETED,
00046     ARRAY_QUOTED_ELEM_STARTED,
00047     ARRAY_QUOTED_ELEM_COMPLETED,
00048     ARRAY_ELEM_DELIMITED,
00049     ARRAY_LEVEL_COMPLETED,
00050     ARRAY_LEVEL_DELIMITED
00051 } ArrayParseState;
00052 
00053 /* Working state for array_iterate() */
00054 typedef struct ArrayIteratorData
00055 {
00056     /* basic info about the array, set up during array_create_iterator() */
00057     ArrayType  *arr;            /* array we're iterating through */
00058     bits8      *nullbitmap;     /* its null bitmap, if any */
00059     int         nitems;         /* total number of elements in array */
00060     int16       typlen;         /* element type's length */
00061     bool        typbyval;       /* element type's byval property */
00062     char        typalign;       /* element type's align property */
00063 
00064     /* information about the requested slice size */
00065     int         slice_ndim;     /* slice dimension, or 0 if not slicing */
00066     int         slice_len;      /* number of elements per slice */
00067     int        *slice_dims;     /* slice dims array */
00068     int        *slice_lbound;   /* slice lbound array */
00069     Datum      *slice_values;   /* workspace of length slice_len */
00070     bool       *slice_nulls;    /* workspace of length slice_len */
00071 
00072     /* current position information, updated on each iteration */
00073     char       *data_ptr;       /* our current position in the array */
00074     int         current_item;   /* the item # we're at in the array */
00075 }   ArrayIteratorData;
00076 
00077 static bool array_isspace(char ch);
00078 static int  ArrayCount(const char *str, int *dim, char typdelim);
00079 static void ReadArrayStr(char *arrayStr, const char *origStr,
00080              int nitems, int ndim, int *dim,
00081              FmgrInfo *inputproc, Oid typioparam, int32 typmod,
00082              char typdelim,
00083              int typlen, bool typbyval, char typalign,
00084              Datum *values, bool *nulls,
00085              bool *hasnulls, int32 *nbytes);
00086 static void ReadArrayBinary(StringInfo buf, int nitems,
00087                 FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
00088                 int typlen, bool typbyval, char typalign,
00089                 Datum *values, bool *nulls,
00090                 bool *hasnulls, int32 *nbytes);
00091 static void CopyArrayEls(ArrayType *array,
00092              Datum *values, bool *nulls, int nitems,
00093              int typlen, bool typbyval, char typalign,
00094              bool freedata);
00095 static bool array_get_isnull(const bits8 *nullbitmap, int offset);
00096 static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
00097 static Datum ArrayCast(char *value, bool byval, int len);
00098 static int ArrayCastAndSet(Datum src,
00099                 int typlen, bool typbyval, char typalign,
00100                 char *dest);
00101 static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
00102            int typlen, bool typbyval, char typalign);
00103 static int array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,
00104                   int nitems, int typlen, bool typbyval, char typalign);
00105 static int array_copy(char *destptr, int nitems,
00106            char *srcptr, int offset, bits8 *nullbitmap,
00107            int typlen, bool typbyval, char typalign);
00108 static int array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
00109                  int ndim, int *dim, int *lb,
00110                  int *st, int *endp,
00111                  int typlen, bool typbyval, char typalign);
00112 static void array_extract_slice(ArrayType *newarray,
00113                     int ndim, int *dim, int *lb,
00114                     char *arraydataptr, bits8 *arraynullsptr,
00115                     int *st, int *endp,
00116                     int typlen, bool typbyval, char typalign);
00117 static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
00118                    ArrayType *srcArray,
00119                    int ndim, int *dim, int *lb,
00120                    int *st, int *endp,
00121                    int typlen, bool typbyval, char typalign);
00122 static int  array_cmp(FunctionCallInfo fcinfo);
00123 static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbv, int nbytes,
00124                       Oid elmtype, int dataoffset);
00125 static ArrayType *array_fill_internal(ArrayType *dims, ArrayType *lbs,
00126                     Datum value, bool isnull, Oid elmtype,
00127                     FunctionCallInfo fcinfo);
00128 static ArrayType *array_replace_internal(ArrayType *array,
00129                        Datum search, bool search_isnull,
00130                        Datum replace, bool replace_isnull,
00131                        bool remove, Oid collation,
00132                        FunctionCallInfo fcinfo);
00133 
00134 
00135 /*
00136  * array_in :
00137  *        converts an array from the external format in "string" to
00138  *        its internal format.
00139  *
00140  * return value :
00141  *        the internal representation of the input array
00142  */
00143 Datum
00144 array_in(PG_FUNCTION_ARGS)
00145 {
00146     char       *string = PG_GETARG_CSTRING(0);  /* external form */
00147     Oid         element_type = PG_GETARG_OID(1);        /* type of an array
00148                                                          * element */
00149     int32       typmod = PG_GETARG_INT32(2);    /* typmod for array elements */
00150     int         typlen;
00151     bool        typbyval;
00152     char        typalign;
00153     char        typdelim;
00154     Oid         typioparam;
00155     char       *string_save,
00156                *p;
00157     int         i,
00158                 nitems;
00159     Datum      *dataPtr;
00160     bool       *nullsPtr;
00161     bool        hasnulls;
00162     int32       nbytes;
00163     int32       dataoffset;
00164     ArrayType  *retval;
00165     int         ndim,
00166                 dim[MAXDIM],
00167                 lBound[MAXDIM];
00168     ArrayMetaState *my_extra;
00169 
00170     /*
00171      * We arrange to look up info about element type, including its input
00172      * conversion proc, only once per series of calls, assuming the element
00173      * type doesn't change underneath us.
00174      */
00175     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
00176     if (my_extra == NULL)
00177     {
00178         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
00179                                                       sizeof(ArrayMetaState));
00180         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
00181         my_extra->element_type = ~element_type;
00182     }
00183 
00184     if (my_extra->element_type != element_type)
00185     {
00186         /*
00187          * Get info about element type, including its input conversion proc
00188          */
00189         get_type_io_data(element_type, IOFunc_input,
00190                          &my_extra->typlen, &my_extra->typbyval,
00191                          &my_extra->typalign, &my_extra->typdelim,
00192                          &my_extra->typioparam, &my_extra->typiofunc);
00193         fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
00194                       fcinfo->flinfo->fn_mcxt);
00195         my_extra->element_type = element_type;
00196     }
00197     typlen = my_extra->typlen;
00198     typbyval = my_extra->typbyval;
00199     typalign = my_extra->typalign;
00200     typdelim = my_extra->typdelim;
00201     typioparam = my_extra->typioparam;
00202 
00203     /* Make a modifiable copy of the input */
00204     string_save = pstrdup(string);
00205 
00206     /*
00207      * If the input string starts with dimension info, read and use that.
00208      * Otherwise, we require the input to be in curly-brace style, and we
00209      * prescan the input to determine dimensions.
00210      *
00211      * Dimension info takes the form of one or more [n] or [m:n] items. The
00212      * outer loop iterates once per dimension item.
00213      */
00214     p = string_save;
00215     ndim = 0;
00216     for (;;)
00217     {
00218         char       *q;
00219         int         ub;
00220 
00221         /*
00222          * Note: we currently allow whitespace between, but not within,
00223          * dimension items.
00224          */
00225         while (array_isspace(*p))
00226             p++;
00227         if (*p != '[')
00228             break;              /* no more dimension items */
00229         p++;
00230         if (ndim >= MAXDIM)
00231             ereport(ERROR,
00232                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
00233                      errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
00234                             ndim + 1, MAXDIM)));
00235 
00236         for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
00237         if (q == p)             /* no digits? */
00238             ereport(ERROR,
00239                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00240                      errmsg("missing dimension value")));
00241 
00242         if (*q == ':')
00243         {
00244             /* [m:n] format */
00245             *q = '\0';
00246             lBound[ndim] = atoi(p);
00247             p = q + 1;
00248             for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
00249             if (q == p)         /* no digits? */
00250                 ereport(ERROR,
00251                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00252                          errmsg("missing dimension value")));
00253         }
00254         else
00255         {
00256             /* [n] format */
00257             lBound[ndim] = 1;
00258         }
00259         if (*q != ']')
00260             ereport(ERROR,
00261                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00262                      errmsg("missing \"]\" in array dimensions")));
00263 
00264         *q = '\0';
00265         ub = atoi(p);
00266         p = q + 1;
00267         if (ub < lBound[ndim])
00268             ereport(ERROR,
00269                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
00270                      errmsg("upper bound cannot be less than lower bound")));
00271 
00272         dim[ndim] = ub - lBound[ndim] + 1;
00273         ndim++;
00274     }
00275 
00276     if (ndim == 0)
00277     {
00278         /* No array dimensions, so intuit dimensions from brace structure */
00279         if (*p != '{')
00280             ereport(ERROR,
00281                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00282                      errmsg("array value must start with \"{\" or dimension information")));
00283         ndim = ArrayCount(p, dim, typdelim);
00284         for (i = 0; i < ndim; i++)
00285             lBound[i] = 1;
00286     }
00287     else
00288     {
00289         int         ndim_braces,
00290                     dim_braces[MAXDIM];
00291 
00292         /* If array dimensions are given, expect '=' operator */
00293         if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
00294             ereport(ERROR,
00295                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00296                      errmsg("missing assignment operator")));
00297         p += strlen(ASSGN);
00298         while (array_isspace(*p))
00299             p++;
00300 
00301         /*
00302          * intuit dimensions from brace structure -- it better match what we
00303          * were given
00304          */
00305         if (*p != '{')
00306             ereport(ERROR,
00307                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00308                      errmsg("array value must start with \"{\" or dimension information")));
00309         ndim_braces = ArrayCount(p, dim_braces, typdelim);
00310         if (ndim_braces != ndim)
00311             ereport(ERROR,
00312                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00313                 errmsg("array dimensions incompatible with array literal")));
00314         for (i = 0; i < ndim; ++i)
00315         {
00316             if (dim[i] != dim_braces[i])
00317                 ereport(ERROR,
00318                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00319                 errmsg("array dimensions incompatible with array literal")));
00320         }
00321     }
00322 
00323 #ifdef ARRAYDEBUG
00324     printf("array_in- ndim %d (", ndim);
00325     for (i = 0; i < ndim; i++)
00326     {
00327         printf(" %d", dim[i]);
00328     };
00329     printf(") for %s\n", string);
00330 #endif
00331 
00332     /* This checks for overflow of the array dimensions */
00333     nitems = ArrayGetNItems(ndim, dim);
00334     /* Empty array? */
00335     if (nitems == 0)
00336         PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
00337 
00338     dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
00339     nullsPtr = (bool *) palloc(nitems * sizeof(bool));
00340     ReadArrayStr(p, string,
00341                  nitems, ndim, dim,
00342                  &my_extra->proc, typioparam, typmod,
00343                  typdelim,
00344                  typlen, typbyval, typalign,
00345                  dataPtr, nullsPtr,
00346                  &hasnulls, &nbytes);
00347     if (hasnulls)
00348     {
00349         dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
00350         nbytes += dataoffset;
00351     }
00352     else
00353     {
00354         dataoffset = 0;         /* marker for no null bitmap */
00355         nbytes += ARR_OVERHEAD_NONULLS(ndim);
00356     }
00357     retval = (ArrayType *) palloc0(nbytes);
00358     SET_VARSIZE(retval, nbytes);
00359     retval->ndim = ndim;
00360     retval->dataoffset = dataoffset;
00361 
00362     /*
00363      * This comes from the array's pg_type.typelem (which points to the base
00364      * data type's pg_type.oid) and stores system oids in user tables. This
00365      * oid must be preserved by binary upgrades.
00366      */
00367     retval->elemtype = element_type;
00368     memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
00369     memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
00370 
00371     CopyArrayEls(retval,
00372                  dataPtr, nullsPtr, nitems,
00373                  typlen, typbyval, typalign,
00374                  true);
00375 
00376     pfree(dataPtr);
00377     pfree(nullsPtr);
00378     pfree(string_save);
00379 
00380     PG_RETURN_ARRAYTYPE_P(retval);
00381 }
00382 
00383 /*
00384  * array_isspace() --- a non-locale-dependent isspace()
00385  *
00386  * We used to use isspace() for parsing array values, but that has
00387  * undesirable results: an array value might be silently interpreted
00388  * differently depending on the locale setting.  Now we just hard-wire
00389  * the traditional ASCII definition of isspace().
00390  */
00391 static bool
00392 array_isspace(char ch)
00393 {
00394     if (ch == ' ' ||
00395         ch == '\t' ||
00396         ch == '\n' ||
00397         ch == '\r' ||
00398         ch == '\v' ||
00399         ch == '\f')
00400         return true;
00401     return false;
00402 }
00403 
00404 /*
00405  * ArrayCount
00406  *   Determines the dimensions for an array string.
00407  *
00408  * Returns number of dimensions as function result.  The axis lengths are
00409  * returned in dim[], which must be of size MAXDIM.
00410  */
00411 static int
00412 ArrayCount(const char *str, int *dim, char typdelim)
00413 {
00414     int         nest_level = 0,
00415                 i;
00416     int         ndim = 1,
00417                 temp[MAXDIM],
00418                 nelems[MAXDIM],
00419                 nelems_last[MAXDIM];
00420     bool        in_quotes = false;
00421     bool        eoArray = false;
00422     bool        empty_array = true;
00423     const char *ptr;
00424     ArrayParseState parse_state = ARRAY_NO_LEVEL;
00425 
00426     for (i = 0; i < MAXDIM; ++i)
00427     {
00428         temp[i] = dim[i] = 0;
00429         nelems_last[i] = nelems[i] = 1;
00430     }
00431 
00432     ptr = str;
00433     while (!eoArray)
00434     {
00435         bool        itemdone = false;
00436 
00437         while (!itemdone)
00438         {
00439             if (parse_state == ARRAY_ELEM_STARTED ||
00440                 parse_state == ARRAY_QUOTED_ELEM_STARTED)
00441                 empty_array = false;
00442 
00443             switch (*ptr)
00444             {
00445                 case '\0':
00446                     /* Signal a premature end of the string */
00447                     ereport(ERROR,
00448                             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00449                              errmsg("malformed array literal: \"%s\"", str)));
00450                     break;
00451                 case '\\':
00452 
00453                     /*
00454                      * An escape must be after a level start, after an element
00455                      * start, or after an element delimiter. In any case we
00456                      * now must be past an element start.
00457                      */
00458                     if (parse_state != ARRAY_LEVEL_STARTED &&
00459                         parse_state != ARRAY_ELEM_STARTED &&
00460                         parse_state != ARRAY_QUOTED_ELEM_STARTED &&
00461                         parse_state != ARRAY_ELEM_DELIMITED)
00462                         ereport(ERROR,
00463                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00464                             errmsg("malformed array literal: \"%s\"", str)));
00465                     if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
00466                         parse_state = ARRAY_ELEM_STARTED;
00467                     /* skip the escaped character */
00468                     if (*(ptr + 1))
00469                         ptr++;
00470                     else
00471                         ereport(ERROR,
00472                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00473                             errmsg("malformed array literal: \"%s\"", str)));
00474                     break;
00475                 case '\"':
00476 
00477                     /*
00478                      * A quote must be after a level start, after a quoted
00479                      * element start, or after an element delimiter. In any
00480                      * case we now must be past an element start.
00481                      */
00482                     if (parse_state != ARRAY_LEVEL_STARTED &&
00483                         parse_state != ARRAY_QUOTED_ELEM_STARTED &&
00484                         parse_state != ARRAY_ELEM_DELIMITED)
00485                         ereport(ERROR,
00486                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00487                             errmsg("malformed array literal: \"%s\"", str)));
00488                     in_quotes = !in_quotes;
00489                     if (in_quotes)
00490                         parse_state = ARRAY_QUOTED_ELEM_STARTED;
00491                     else
00492                         parse_state = ARRAY_QUOTED_ELEM_COMPLETED;
00493                     break;
00494                 case '{':
00495                     if (!in_quotes)
00496                     {
00497                         /*
00498                          * A left brace can occur if no nesting has occurred
00499                          * yet, after a level start, or after a level
00500                          * delimiter.
00501                          */
00502                         if (parse_state != ARRAY_NO_LEVEL &&
00503                             parse_state != ARRAY_LEVEL_STARTED &&
00504                             parse_state != ARRAY_LEVEL_DELIMITED)
00505                             ereport(ERROR,
00506                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00507                             errmsg("malformed array literal: \"%s\"", str)));
00508                         parse_state = ARRAY_LEVEL_STARTED;
00509                         if (nest_level >= MAXDIM)
00510                             ereport(ERROR,
00511                                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
00512                                      errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
00513                                             nest_level + 1, MAXDIM)));
00514                         temp[nest_level] = 0;
00515                         nest_level++;
00516                         if (ndim < nest_level)
00517                             ndim = nest_level;
00518                     }
00519                     break;
00520                 case '}':
00521                     if (!in_quotes)
00522                     {
00523                         /*
00524                          * A right brace can occur after an element start, an
00525                          * element completion, a quoted element completion, or
00526                          * a level completion.
00527                          */
00528                         if (parse_state != ARRAY_ELEM_STARTED &&
00529                             parse_state != ARRAY_ELEM_COMPLETED &&
00530                             parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
00531                             parse_state != ARRAY_LEVEL_COMPLETED &&
00532                             !(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
00533                             ereport(ERROR,
00534                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00535                             errmsg("malformed array literal: \"%s\"", str)));
00536                         parse_state = ARRAY_LEVEL_COMPLETED;
00537                         if (nest_level == 0)
00538                             ereport(ERROR,
00539                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00540                             errmsg("malformed array literal: \"%s\"", str)));
00541                         nest_level--;
00542 
00543                         if ((nelems_last[nest_level] != 1) &&
00544                             (nelems[nest_level] != nelems_last[nest_level]))
00545                             ereport(ERROR,
00546                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00547                                 errmsg("multidimensional arrays must have "
00548                                        "array expressions with matching "
00549                                        "dimensions")));
00550                         nelems_last[nest_level] = nelems[nest_level];
00551                         nelems[nest_level] = 1;
00552                         if (nest_level == 0)
00553                             eoArray = itemdone = true;
00554                         else
00555                         {
00556                             /*
00557                              * We don't set itemdone here; see comments in
00558                              * ReadArrayStr
00559                              */
00560                             temp[nest_level - 1]++;
00561                         }
00562                     }
00563                     break;
00564                 default:
00565                     if (!in_quotes)
00566                     {
00567                         if (*ptr == typdelim)
00568                         {
00569                             /*
00570                              * Delimiters can occur after an element start, an
00571                              * element completion, a quoted element
00572                              * completion, or a level completion.
00573                              */
00574                             if (parse_state != ARRAY_ELEM_STARTED &&
00575                                 parse_state != ARRAY_ELEM_COMPLETED &&
00576                                 parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
00577                                 parse_state != ARRAY_LEVEL_COMPLETED)
00578                                 ereport(ERROR,
00579                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00580                                  errmsg("malformed array literal: \"%s\"", str)));
00581                             if (parse_state == ARRAY_LEVEL_COMPLETED)
00582                                 parse_state = ARRAY_LEVEL_DELIMITED;
00583                             else
00584                                 parse_state = ARRAY_ELEM_DELIMITED;
00585                             itemdone = true;
00586                             nelems[nest_level - 1]++;
00587                         }
00588                         else if (!array_isspace(*ptr))
00589                         {
00590                             /*
00591                              * Other non-space characters must be after a
00592                              * level start, after an element start, or after
00593                              * an element delimiter. In any case we now must
00594                              * be past an element start.
00595                              */
00596                             if (parse_state != ARRAY_LEVEL_STARTED &&
00597                                 parse_state != ARRAY_ELEM_STARTED &&
00598                                 parse_state != ARRAY_ELEM_DELIMITED)
00599                                 ereport(ERROR,
00600                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00601                                  errmsg("malformed array literal: \"%s\"", str)));
00602                             parse_state = ARRAY_ELEM_STARTED;
00603                         }
00604                     }
00605                     break;
00606             }
00607             if (!itemdone)
00608                 ptr++;
00609         }
00610         temp[ndim - 1]++;
00611         ptr++;
00612     }
00613 
00614     /* only whitespace is allowed after the closing brace */
00615     while (*ptr)
00616     {
00617         if (!array_isspace(*ptr++))
00618             ereport(ERROR,
00619                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00620                      errmsg("malformed array literal: \"%s\"", str)));
00621     }
00622 
00623     /* special case for an empty array */
00624     if (empty_array)
00625         return 0;
00626 
00627     for (i = 0; i < ndim; ++i)
00628         dim[i] = temp[i];
00629 
00630     return ndim;
00631 }
00632 
00633 /*
00634  * ReadArrayStr :
00635  *   parses the array string pointed to by "arrayStr" and converts the values
00636  *   to internal format.  Unspecified elements are initialized to nulls.
00637  *   The array dimensions must already have been determined.
00638  *
00639  * Inputs:
00640  *  arrayStr: the string to parse.
00641  *            CAUTION: the contents of "arrayStr" will be modified!
00642  *  origStr: the unmodified input string, used only in error messages.
00643  *  nitems: total number of array elements, as already determined.
00644  *  ndim: number of array dimensions
00645  *  dim[]: array axis lengths
00646  *  inputproc: type-specific input procedure for element datatype.
00647  *  typioparam, typmod: auxiliary values to pass to inputproc.
00648  *  typdelim: the value delimiter (type-specific).
00649  *  typlen, typbyval, typalign: storage parameters of element datatype.
00650  *
00651  * Outputs:
00652  *  values[]: filled with converted data values.
00653  *  nulls[]: filled with is-null markers.
00654  *  *hasnulls: set TRUE iff there are any null elements.
00655  *  *nbytes: set to total size of data area needed (including alignment
00656  *      padding but not including array header overhead).
00657  *
00658  * Note that values[] and nulls[] are allocated by the caller, and must have
00659  * nitems elements.
00660  */
00661 static void
00662 ReadArrayStr(char *arrayStr,
00663              const char *origStr,
00664              int nitems,
00665              int ndim,
00666              int *dim,
00667              FmgrInfo *inputproc,
00668              Oid typioparam,
00669              int32 typmod,
00670              char typdelim,
00671              int typlen,
00672              bool typbyval,
00673              char typalign,
00674              Datum *values,
00675              bool *nulls,
00676              bool *hasnulls,
00677              int32 *nbytes)
00678 {
00679     int         i,
00680                 nest_level = 0;
00681     char       *srcptr;
00682     bool        in_quotes = false;
00683     bool        eoArray = false;
00684     bool        hasnull;
00685     int32       totbytes;
00686     int         indx[MAXDIM],
00687                 prod[MAXDIM];
00688 
00689     mda_get_prod(ndim, dim, prod);
00690     MemSet(indx, 0, sizeof(indx));
00691 
00692     /* Initialize is-null markers to true */
00693     memset(nulls, true, nitems * sizeof(bool));
00694 
00695     /*
00696      * We have to remove " and \ characters to create a clean item value to
00697      * pass to the datatype input routine.  We overwrite each item value
00698      * in-place within arrayStr to do this.  srcptr is the current scan point,
00699      * and dstptr is where we are copying to.
00700      *
00701      * We also want to suppress leading and trailing unquoted whitespace. We
00702      * use the leadingspace flag to suppress leading space.  Trailing space is
00703      * tracked by using dstendptr to point to the last significant output
00704      * character.
00705      *
00706      * The error checking in this routine is mostly pro-forma, since we expect
00707      * that ArrayCount() already validated the string.
00708      */
00709     srcptr = arrayStr;
00710     while (!eoArray)
00711     {
00712         bool        itemdone = false;
00713         bool        leadingspace = true;
00714         bool        hasquoting = false;
00715         char       *itemstart;
00716         char       *dstptr;
00717         char       *dstendptr;
00718 
00719         i = -1;
00720         itemstart = dstptr = dstendptr = srcptr;
00721 
00722         while (!itemdone)
00723         {
00724             switch (*srcptr)
00725             {
00726                 case '\0':
00727                     /* Signal a premature end of the string */
00728                     ereport(ERROR,
00729                             (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00730                              errmsg("malformed array literal: \"%s\"",
00731                                     origStr)));
00732                     break;
00733                 case '\\':
00734                     /* Skip backslash, copy next character as-is. */
00735                     srcptr++;
00736                     if (*srcptr == '\0')
00737                         ereport(ERROR,
00738                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00739                                  errmsg("malformed array literal: \"%s\"",
00740                                         origStr)));
00741                     *dstptr++ = *srcptr++;
00742                     /* Treat the escaped character as non-whitespace */
00743                     leadingspace = false;
00744                     dstendptr = dstptr;
00745                     hasquoting = true;  /* can't be a NULL marker */
00746                     break;
00747                 case '\"':
00748                     in_quotes = !in_quotes;
00749                     if (in_quotes)
00750                         leadingspace = false;
00751                     else
00752                     {
00753                         /*
00754                          * Advance dstendptr when we exit in_quotes; this
00755                          * saves having to do it in all the other in_quotes
00756                          * cases.
00757                          */
00758                         dstendptr = dstptr;
00759                     }
00760                     hasquoting = true;  /* can't be a NULL marker */
00761                     srcptr++;
00762                     break;
00763                 case '{':
00764                     if (!in_quotes)
00765                     {
00766                         if (nest_level >= ndim)
00767                             ereport(ERROR,
00768                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00769                                 errmsg("malformed array literal: \"%s\"",
00770                                        origStr)));
00771                         nest_level++;
00772                         indx[nest_level - 1] = 0;
00773                         srcptr++;
00774                     }
00775                     else
00776                         *dstptr++ = *srcptr++;
00777                     break;
00778                 case '}':
00779                     if (!in_quotes)
00780                     {
00781                         if (nest_level == 0)
00782                             ereport(ERROR,
00783                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00784                                 errmsg("malformed array literal: \"%s\"",
00785                                        origStr)));
00786                         if (i == -1)
00787                             i = ArrayGetOffset0(ndim, indx, prod);
00788                         indx[nest_level - 1] = 0;
00789                         nest_level--;
00790                         if (nest_level == 0)
00791                             eoArray = itemdone = true;
00792                         else
00793                             indx[nest_level - 1]++;
00794                         srcptr++;
00795                     }
00796                     else
00797                         *dstptr++ = *srcptr++;
00798                     break;
00799                 default:
00800                     if (in_quotes)
00801                         *dstptr++ = *srcptr++;
00802                     else if (*srcptr == typdelim)
00803                     {
00804                         if (i == -1)
00805                             i = ArrayGetOffset0(ndim, indx, prod);
00806                         itemdone = true;
00807                         indx[ndim - 1]++;
00808                         srcptr++;
00809                     }
00810                     else if (array_isspace(*srcptr))
00811                     {
00812                         /*
00813                          * If leading space, drop it immediately.  Else, copy
00814                          * but don't advance dstendptr.
00815                          */
00816                         if (leadingspace)
00817                             srcptr++;
00818                         else
00819                             *dstptr++ = *srcptr++;
00820                     }
00821                     else
00822                     {
00823                         *dstptr++ = *srcptr++;
00824                         leadingspace = false;
00825                         dstendptr = dstptr;
00826                     }
00827                     break;
00828             }
00829         }
00830 
00831         Assert(dstptr < srcptr);
00832         *dstendptr = '\0';
00833 
00834         if (i < 0 || i >= nitems)
00835             ereport(ERROR,
00836                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00837                      errmsg("malformed array literal: \"%s\"",
00838                             origStr)));
00839 
00840         if (Array_nulls && !hasquoting &&
00841             pg_strcasecmp(itemstart, "NULL") == 0)
00842         {
00843             /* it's a NULL item */
00844             values[i] = InputFunctionCall(inputproc, NULL,
00845                                           typioparam, typmod);
00846             nulls[i] = true;
00847         }
00848         else
00849         {
00850             values[i] = InputFunctionCall(inputproc, itemstart,
00851                                           typioparam, typmod);
00852             nulls[i] = false;
00853         }
00854     }
00855 
00856     /*
00857      * Check for nulls, compute total data space needed
00858      */
00859     hasnull = false;
00860     totbytes = 0;
00861     for (i = 0; i < nitems; i++)
00862     {
00863         if (nulls[i])
00864             hasnull = true;
00865         else
00866         {
00867             /* let's just make sure data is not toasted */
00868             if (typlen == -1)
00869                 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
00870             totbytes = att_addlength_datum(totbytes, typlen, values[i]);
00871             totbytes = att_align_nominal(totbytes, typalign);
00872             /* check for overflow of total request */
00873             if (!AllocSizeIsValid(totbytes))
00874                 ereport(ERROR,
00875                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
00876                          errmsg("array size exceeds the maximum allowed (%d)",
00877                                 (int) MaxAllocSize)));
00878         }
00879     }
00880     *hasnulls = hasnull;
00881     *nbytes = totbytes;
00882 }
00883 
00884 
00885 /*
00886  * Copy data into an array object from a temporary array of Datums.
00887  *
00888  * array: array object (with header fields already filled in)
00889  * values: array of Datums to be copied
00890  * nulls: array of is-null flags (can be NULL if no nulls)
00891  * nitems: number of Datums to be copied
00892  * typbyval, typlen, typalign: info about element datatype
00893  * freedata: if TRUE and element type is pass-by-ref, pfree data values
00894  * referenced by Datums after copying them.
00895  *
00896  * If the input data is of varlena type, the caller must have ensured that
00897  * the values are not toasted.  (Doing it here doesn't work since the
00898  * caller has already allocated space for the array...)
00899  */
00900 static void
00901 CopyArrayEls(ArrayType *array,
00902              Datum *values,
00903              bool *nulls,
00904              int nitems,
00905              int typlen,
00906              bool typbyval,
00907              char typalign,
00908              bool freedata)
00909 {
00910     char       *p = ARR_DATA_PTR(array);
00911     bits8      *bitmap = ARR_NULLBITMAP(array);
00912     int         bitval = 0;
00913     int         bitmask = 1;
00914     int         i;
00915 
00916     if (typbyval)
00917         freedata = false;
00918 
00919     for (i = 0; i < nitems; i++)
00920     {
00921         if (nulls && nulls[i])
00922         {
00923             if (!bitmap)        /* shouldn't happen */
00924                 elog(ERROR, "null array element where not supported");
00925             /* bitmap bit stays 0 */
00926         }
00927         else
00928         {
00929             bitval |= bitmask;
00930             p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
00931             if (freedata)
00932                 pfree(DatumGetPointer(values[i]));
00933         }
00934         if (bitmap)
00935         {
00936             bitmask <<= 1;
00937             if (bitmask == 0x100)
00938             {
00939                 *bitmap++ = bitval;
00940                 bitval = 0;
00941                 bitmask = 1;
00942             }
00943         }
00944     }
00945 
00946     if (bitmap && bitmask != 1)
00947         *bitmap = bitval;
00948 }
00949 
00950 /*
00951  * array_out :
00952  *         takes the internal representation of an array and returns a string
00953  *        containing the array in its external format.
00954  */
00955 Datum
00956 array_out(PG_FUNCTION_ARGS)
00957 {
00958     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
00959     Oid         element_type = ARR_ELEMTYPE(v);
00960     int         typlen;
00961     bool        typbyval;
00962     char        typalign;
00963     char        typdelim;
00964     char       *p,
00965                *tmp,
00966                *retval,
00967               **values,
00968                 dims_str[(MAXDIM * 33) + 2];
00969 
00970     /*
00971      * 33 per dim since we assume 15 digits per number + ':' +'[]'
00972      *
00973      * +2 allows for assignment operator + trailing null
00974      */
00975     bits8      *bitmap;
00976     int         bitmask;
00977     bool       *needquotes,
00978                 needdims = false;
00979     int         nitems,
00980                 overall_length,
00981                 i,
00982                 j,
00983                 k,
00984                 indx[MAXDIM];
00985     int         ndim,
00986                *dims,
00987                *lb;
00988     ArrayMetaState *my_extra;
00989 
00990     /*
00991      * We arrange to look up info about element type, including its output
00992      * conversion proc, only once per series of calls, assuming the element
00993      * type doesn't change underneath us.
00994      */
00995     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
00996     if (my_extra == NULL)
00997     {
00998         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
00999                                                       sizeof(ArrayMetaState));
01000         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
01001         my_extra->element_type = ~element_type;
01002     }
01003 
01004     if (my_extra->element_type != element_type)
01005     {
01006         /*
01007          * Get info about element type, including its output conversion proc
01008          */
01009         get_type_io_data(element_type, IOFunc_output,
01010                          &my_extra->typlen, &my_extra->typbyval,
01011                          &my_extra->typalign, &my_extra->typdelim,
01012                          &my_extra->typioparam, &my_extra->typiofunc);
01013         fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
01014                       fcinfo->flinfo->fn_mcxt);
01015         my_extra->element_type = element_type;
01016     }
01017     typlen = my_extra->typlen;
01018     typbyval = my_extra->typbyval;
01019     typalign = my_extra->typalign;
01020     typdelim = my_extra->typdelim;
01021 
01022     ndim = ARR_NDIM(v);
01023     dims = ARR_DIMS(v);
01024     lb = ARR_LBOUND(v);
01025     nitems = ArrayGetNItems(ndim, dims);
01026 
01027     if (nitems == 0)
01028     {
01029         retval = pstrdup("{}");
01030         PG_RETURN_CSTRING(retval);
01031     }
01032 
01033     /*
01034      * we will need to add explicit dimensions if any dimension has a lower
01035      * bound other than one
01036      */
01037     for (i = 0; i < ndim; i++)
01038     {
01039         if (lb[i] != 1)
01040         {
01041             needdims = true;
01042             break;
01043         }
01044     }
01045 
01046     /*
01047      * Convert all values to string form, count total space needed (including
01048      * any overhead such as escaping backslashes), and detect whether each
01049      * item needs double quotes.
01050      */
01051     values = (char **) palloc(nitems * sizeof(char *));
01052     needquotes = (bool *) palloc(nitems * sizeof(bool));
01053     overall_length = 1;         /* don't forget to count \0 at end. */
01054 
01055     p = ARR_DATA_PTR(v);
01056     bitmap = ARR_NULLBITMAP(v);
01057     bitmask = 1;
01058 
01059     for (i = 0; i < nitems; i++)
01060     {
01061         bool        needquote;
01062 
01063         /* Get source element, checking for NULL */
01064         if (bitmap && (*bitmap & bitmask) == 0)
01065         {
01066             values[i] = pstrdup("NULL");
01067             overall_length += 4;
01068             needquote = false;
01069         }
01070         else
01071         {
01072             Datum       itemvalue;
01073 
01074             itemvalue = fetch_att(p, typbyval, typlen);
01075             values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
01076             p = att_addlength_pointer(p, typlen, p);
01077             p = (char *) att_align_nominal(p, typalign);
01078 
01079             /* count data plus backslashes; detect chars needing quotes */
01080             if (values[i][0] == '\0')
01081                 needquote = true;       /* force quotes for empty string */
01082             else if (pg_strcasecmp(values[i], "NULL") == 0)
01083                 needquote = true;       /* force quotes for literal NULL */
01084             else
01085                 needquote = false;
01086 
01087             for (tmp = values[i]; *tmp != '\0'; tmp++)
01088             {
01089                 char        ch = *tmp;
01090 
01091                 overall_length += 1;
01092                 if (ch == '"' || ch == '\\')
01093                 {
01094                     needquote = true;
01095                     overall_length += 1;
01096                 }
01097                 else if (ch == '{' || ch == '}' || ch == typdelim ||
01098                          array_isspace(ch))
01099                     needquote = true;
01100             }
01101         }
01102 
01103         needquotes[i] = needquote;
01104 
01105         /* Count the pair of double quotes, if needed */
01106         if (needquote)
01107             overall_length += 2;
01108         /* and the comma */
01109         overall_length += 1;
01110 
01111         /* advance bitmap pointer if any */
01112         if (bitmap)
01113         {
01114             bitmask <<= 1;
01115             if (bitmask == 0x100)
01116             {
01117                 bitmap++;
01118                 bitmask = 1;
01119             }
01120         }
01121     }
01122 
01123     /*
01124      * count total number of curly braces in output string
01125      */
01126     for (i = j = 0, k = 1; i < ndim; i++)
01127         k *= dims[i], j += k;
01128 
01129     dims_str[0] = '\0';
01130 
01131     /* add explicit dimensions if required */
01132     if (needdims)
01133     {
01134         char       *ptr = dims_str;
01135 
01136         for (i = 0; i < ndim; i++)
01137         {
01138             sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
01139             ptr += strlen(ptr);
01140         }
01141         *ptr++ = *ASSGN;
01142         *ptr = '\0';
01143     }
01144 
01145     retval = (char *) palloc(strlen(dims_str) + overall_length + 2 * j);
01146     p = retval;
01147 
01148 #define APPENDSTR(str)  (strcpy(p, (str)), p += strlen(p))
01149 #define APPENDCHAR(ch)  (*p++ = (ch), *p = '\0')
01150 
01151     if (needdims)
01152         APPENDSTR(dims_str);
01153     APPENDCHAR('{');
01154     for (i = 0; i < ndim; i++)
01155         indx[i] = 0;
01156     j = 0;
01157     k = 0;
01158     do
01159     {
01160         for (i = j; i < ndim - 1; i++)
01161             APPENDCHAR('{');
01162 
01163         if (needquotes[k])
01164         {
01165             APPENDCHAR('"');
01166             for (tmp = values[k]; *tmp; tmp++)
01167             {
01168                 char        ch = *tmp;
01169 
01170                 if (ch == '"' || ch == '\\')
01171                     *p++ = '\\';
01172                 *p++ = ch;
01173             }
01174             *p = '\0';
01175             APPENDCHAR('"');
01176         }
01177         else
01178             APPENDSTR(values[k]);
01179         pfree(values[k++]);
01180 
01181         for (i = ndim - 1; i >= 0; i--)
01182         {
01183             indx[i] = (indx[i] + 1) % dims[i];
01184             if (indx[i])
01185             {
01186                 APPENDCHAR(typdelim);
01187                 break;
01188             }
01189             else
01190                 APPENDCHAR('}');
01191         }
01192         j = i;
01193     } while (j != -1);
01194 
01195 #undef APPENDSTR
01196 #undef APPENDCHAR
01197 
01198     pfree(values);
01199     pfree(needquotes);
01200 
01201     PG_RETURN_CSTRING(retval);
01202 }
01203 
01204 /*
01205  * array_recv :
01206  *        converts an array from the external binary format to
01207  *        its internal format.
01208  *
01209  * return value :
01210  *        the internal representation of the input array
01211  */
01212 Datum
01213 array_recv(PG_FUNCTION_ARGS)
01214 {
01215     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
01216     Oid         spec_element_type = PG_GETARG_OID(1);   /* type of an array
01217                                                          * element */
01218     int32       typmod = PG_GETARG_INT32(2);    /* typmod for array elements */
01219     Oid         element_type;
01220     int         typlen;
01221     bool        typbyval;
01222     char        typalign;
01223     Oid         typioparam;
01224     int         i,
01225                 nitems;
01226     Datum      *dataPtr;
01227     bool       *nullsPtr;
01228     bool        hasnulls;
01229     int32       nbytes;
01230     int32       dataoffset;
01231     ArrayType  *retval;
01232     int         ndim,
01233                 flags,
01234                 dim[MAXDIM],
01235                 lBound[MAXDIM];
01236     ArrayMetaState *my_extra;
01237 
01238     /* Get the array header information */
01239     ndim = pq_getmsgint(buf, 4);
01240     if (ndim < 0)               /* we do allow zero-dimension arrays */
01241         ereport(ERROR,
01242                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
01243                  errmsg("invalid number of dimensions: %d", ndim)));
01244     if (ndim > MAXDIM)
01245         ereport(ERROR,
01246                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
01247                  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
01248                         ndim, MAXDIM)));
01249 
01250     flags = pq_getmsgint(buf, 4);
01251     if (flags != 0 && flags != 1)
01252         ereport(ERROR,
01253                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
01254                  errmsg("invalid array flags")));
01255 
01256     element_type = pq_getmsgint(buf, sizeof(Oid));
01257     if (element_type != spec_element_type)
01258     {
01259         /* XXX Can we allow taking the input element type in any cases? */
01260         ereport(ERROR,
01261                 (errcode(ERRCODE_DATATYPE_MISMATCH),
01262                  errmsg("wrong element type")));
01263     }
01264 
01265     for (i = 0; i < ndim; i++)
01266     {
01267         dim[i] = pq_getmsgint(buf, 4);
01268         lBound[i] = pq_getmsgint(buf, 4);
01269 
01270         /*
01271          * Check overflow of upper bound. (ArrayNItems() below checks that
01272          * dim[i] >= 0)
01273          */
01274         if (dim[i] != 0)
01275         {
01276             int         ub = lBound[i] + dim[i] - 1;
01277 
01278             if (lBound[i] > ub)
01279                 ereport(ERROR,
01280                         (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
01281                          errmsg("integer out of range")));
01282         }
01283     }
01284 
01285     /* This checks for overflow of array dimensions */
01286     nitems = ArrayGetNItems(ndim, dim);
01287 
01288     /*
01289      * We arrange to look up info about element type, including its receive
01290      * conversion proc, only once per series of calls, assuming the element
01291      * type doesn't change underneath us.
01292      */
01293     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
01294     if (my_extra == NULL)
01295     {
01296         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
01297                                                       sizeof(ArrayMetaState));
01298         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
01299         my_extra->element_type = ~element_type;
01300     }
01301 
01302     if (my_extra->element_type != element_type)
01303     {
01304         /* Get info about element type, including its receive proc */
01305         get_type_io_data(element_type, IOFunc_receive,
01306                          &my_extra->typlen, &my_extra->typbyval,
01307                          &my_extra->typalign, &my_extra->typdelim,
01308                          &my_extra->typioparam, &my_extra->typiofunc);
01309         if (!OidIsValid(my_extra->typiofunc))
01310             ereport(ERROR,
01311                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
01312                      errmsg("no binary input function available for type %s",
01313                             format_type_be(element_type))));
01314         fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
01315                       fcinfo->flinfo->fn_mcxt);
01316         my_extra->element_type = element_type;
01317     }
01318 
01319     if (nitems == 0)
01320     {
01321         /* Return empty array ... but not till we've validated element_type */
01322         PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
01323     }
01324 
01325     typlen = my_extra->typlen;
01326     typbyval = my_extra->typbyval;
01327     typalign = my_extra->typalign;
01328     typioparam = my_extra->typioparam;
01329 
01330     dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
01331     nullsPtr = (bool *) palloc(nitems * sizeof(bool));
01332     ReadArrayBinary(buf, nitems,
01333                     &my_extra->proc, typioparam, typmod,
01334                     typlen, typbyval, typalign,
01335                     dataPtr, nullsPtr,
01336                     &hasnulls, &nbytes);
01337     if (hasnulls)
01338     {
01339         dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
01340         nbytes += dataoffset;
01341     }
01342     else
01343     {
01344         dataoffset = 0;         /* marker for no null bitmap */
01345         nbytes += ARR_OVERHEAD_NONULLS(ndim);
01346     }
01347     retval = (ArrayType *) palloc0(nbytes);
01348     SET_VARSIZE(retval, nbytes);
01349     retval->ndim = ndim;
01350     retval->dataoffset = dataoffset;
01351     retval->elemtype = element_type;
01352     memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
01353     memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
01354 
01355     CopyArrayEls(retval,
01356                  dataPtr, nullsPtr, nitems,
01357                  typlen, typbyval, typalign,
01358                  true);
01359 
01360     pfree(dataPtr);
01361     pfree(nullsPtr);
01362 
01363     PG_RETURN_ARRAYTYPE_P(retval);
01364 }
01365 
01366 /*
01367  * ReadArrayBinary:
01368  *   collect the data elements of an array being read in binary style.
01369  *
01370  * Inputs:
01371  *  buf: the data buffer to read from.
01372  *  nitems: total number of array elements (already read).
01373  *  receiveproc: type-specific receive procedure for element datatype.
01374  *  typioparam, typmod: auxiliary values to pass to receiveproc.
01375  *  typlen, typbyval, typalign: storage parameters of element datatype.
01376  *
01377  * Outputs:
01378  *  values[]: filled with converted data values.
01379  *  nulls[]: filled with is-null markers.
01380  *  *hasnulls: set TRUE iff there are any null elements.
01381  *  *nbytes: set to total size of data area needed (including alignment
01382  *      padding but not including array header overhead).
01383  *
01384  * Note that values[] and nulls[] are allocated by the caller, and must have
01385  * nitems elements.
01386  */
01387 static void
01388 ReadArrayBinary(StringInfo buf,
01389                 int nitems,
01390                 FmgrInfo *receiveproc,
01391                 Oid typioparam,
01392                 int32 typmod,
01393                 int typlen,
01394                 bool typbyval,
01395                 char typalign,
01396                 Datum *values,
01397                 bool *nulls,
01398                 bool *hasnulls,
01399                 int32 *nbytes)
01400 {
01401     int         i;
01402     bool        hasnull;
01403     int32       totbytes;
01404 
01405     for (i = 0; i < nitems; i++)
01406     {
01407         int         itemlen;
01408         StringInfoData elem_buf;
01409         char        csave;
01410 
01411         /* Get and check the item length */
01412         itemlen = pq_getmsgint(buf, 4);
01413         if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
01414             ereport(ERROR,
01415                     (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
01416                      errmsg("insufficient data left in message")));
01417 
01418         if (itemlen == -1)
01419         {
01420             /* -1 length means NULL */
01421             values[i] = ReceiveFunctionCall(receiveproc, NULL,
01422                                             typioparam, typmod);
01423             nulls[i] = true;
01424             continue;
01425         }
01426 
01427         /*
01428          * Rather than copying data around, we just set up a phony StringInfo
01429          * pointing to the correct portion of the input buffer. We assume we
01430          * can scribble on the input buffer so as to maintain the convention
01431          * that StringInfos have a trailing null.
01432          */
01433         elem_buf.data = &buf->data[buf->cursor];
01434         elem_buf.maxlen = itemlen + 1;
01435         elem_buf.len = itemlen;
01436         elem_buf.cursor = 0;
01437 
01438         buf->cursor += itemlen;
01439 
01440         csave = buf->data[buf->cursor];
01441         buf->data[buf->cursor] = '\0';
01442 
01443         /* Now call the element's receiveproc */
01444         values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
01445                                         typioparam, typmod);
01446         nulls[i] = false;
01447 
01448         /* Trouble if it didn't eat the whole buffer */
01449         if (elem_buf.cursor != itemlen)
01450             ereport(ERROR,
01451                     (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
01452                      errmsg("improper binary format in array element %d",
01453                             i + 1)));
01454 
01455         buf->data[buf->cursor] = csave;
01456     }
01457 
01458     /*
01459      * Check for nulls, compute total data space needed
01460      */
01461     hasnull = false;
01462     totbytes = 0;
01463     for (i = 0; i < nitems; i++)
01464     {
01465         if (nulls[i])
01466             hasnull = true;
01467         else
01468         {
01469             /* let's just make sure data is not toasted */
01470             if (typlen == -1)
01471                 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
01472             totbytes = att_addlength_datum(totbytes, typlen, values[i]);
01473             totbytes = att_align_nominal(totbytes, typalign);
01474             /* check for overflow of total request */
01475             if (!AllocSizeIsValid(totbytes))
01476                 ereport(ERROR,
01477                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
01478                          errmsg("array size exceeds the maximum allowed (%d)",
01479                                 (int) MaxAllocSize)));
01480         }
01481     }
01482     *hasnulls = hasnull;
01483     *nbytes = totbytes;
01484 }
01485 
01486 
01487 /*
01488  * array_send :
01489  *        takes the internal representation of an array and returns a bytea
01490  *        containing the array in its external binary format.
01491  */
01492 Datum
01493 array_send(PG_FUNCTION_ARGS)
01494 {
01495     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
01496     Oid         element_type = ARR_ELEMTYPE(v);
01497     int         typlen;
01498     bool        typbyval;
01499     char        typalign;
01500     char       *p;
01501     bits8      *bitmap;
01502     int         bitmask;
01503     int         nitems,
01504                 i;
01505     int         ndim,
01506                *dim;
01507     StringInfoData buf;
01508     ArrayMetaState *my_extra;
01509 
01510     /*
01511      * We arrange to look up info about element type, including its send
01512      * conversion proc, only once per series of calls, assuming the element
01513      * type doesn't change underneath us.
01514      */
01515     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
01516     if (my_extra == NULL)
01517     {
01518         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
01519                                                       sizeof(ArrayMetaState));
01520         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
01521         my_extra->element_type = ~element_type;
01522     }
01523 
01524     if (my_extra->element_type != element_type)
01525     {
01526         /* Get info about element type, including its send proc */
01527         get_type_io_data(element_type, IOFunc_send,
01528                          &my_extra->typlen, &my_extra->typbyval,
01529                          &my_extra->typalign, &my_extra->typdelim,
01530                          &my_extra->typioparam, &my_extra->typiofunc);
01531         if (!OidIsValid(my_extra->typiofunc))
01532             ereport(ERROR,
01533                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
01534                      errmsg("no binary output function available for type %s",
01535                             format_type_be(element_type))));
01536         fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
01537                       fcinfo->flinfo->fn_mcxt);
01538         my_extra->element_type = element_type;
01539     }
01540     typlen = my_extra->typlen;
01541     typbyval = my_extra->typbyval;
01542     typalign = my_extra->typalign;
01543 
01544     ndim = ARR_NDIM(v);
01545     dim = ARR_DIMS(v);
01546     nitems = ArrayGetNItems(ndim, dim);
01547 
01548     pq_begintypsend(&buf);
01549 
01550     /* Send the array header information */
01551     pq_sendint(&buf, ndim, 4);
01552     pq_sendint(&buf, ARR_HASNULL(v) ? 1 : 0, 4);
01553     pq_sendint(&buf, element_type, sizeof(Oid));
01554     for (i = 0; i < ndim; i++)
01555     {
01556         pq_sendint(&buf, ARR_DIMS(v)[i], 4);
01557         pq_sendint(&buf, ARR_LBOUND(v)[i], 4);
01558     }
01559 
01560     /* Send the array elements using the element's own sendproc */
01561     p = ARR_DATA_PTR(v);
01562     bitmap = ARR_NULLBITMAP(v);
01563     bitmask = 1;
01564 
01565     for (i = 0; i < nitems; i++)
01566     {
01567         /* Get source element, checking for NULL */
01568         if (bitmap && (*bitmap & bitmask) == 0)
01569         {
01570             /* -1 length means a NULL */
01571             pq_sendint(&buf, -1, 4);
01572         }
01573         else
01574         {
01575             Datum       itemvalue;
01576             bytea      *outputbytes;
01577 
01578             itemvalue = fetch_att(p, typbyval, typlen);
01579             outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
01580             pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
01581             pq_sendbytes(&buf, VARDATA(outputbytes),
01582                          VARSIZE(outputbytes) - VARHDRSZ);
01583             pfree(outputbytes);
01584 
01585             p = att_addlength_pointer(p, typlen, p);
01586             p = (char *) att_align_nominal(p, typalign);
01587         }
01588 
01589         /* advance bitmap pointer if any */
01590         if (bitmap)
01591         {
01592             bitmask <<= 1;
01593             if (bitmask == 0x100)
01594             {
01595                 bitmap++;
01596                 bitmask = 1;
01597             }
01598         }
01599     }
01600 
01601     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
01602 }
01603 
01604 /*
01605  * array_ndims :
01606  *        returns the number of dimensions of the array pointed to by "v"
01607  */
01608 Datum
01609 array_ndims(PG_FUNCTION_ARGS)
01610 {
01611     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
01612 
01613     /* Sanity check: does it look like an array at all? */
01614     if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
01615         PG_RETURN_NULL();
01616 
01617     PG_RETURN_INT32(ARR_NDIM(v));
01618 }
01619 
01620 /*
01621  * array_dims :
01622  *        returns the dimensions of the array pointed to by "v", as a "text"
01623  */
01624 Datum
01625 array_dims(PG_FUNCTION_ARGS)
01626 {
01627     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
01628     char       *p;
01629     int         i;
01630     int        *dimv,
01631                *lb;
01632 
01633     /*
01634      * 33 since we assume 15 digits per number + ':' +'[]'
01635      *
01636      * +1 for trailing null
01637      */
01638     char        buf[MAXDIM * 33 + 1];
01639 
01640     /* Sanity check: does it look like an array at all? */
01641     if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
01642         PG_RETURN_NULL();
01643 
01644     dimv = ARR_DIMS(v);
01645     lb = ARR_LBOUND(v);
01646 
01647     p = buf;
01648     for (i = 0; i < ARR_NDIM(v); i++)
01649     {
01650         sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
01651         p += strlen(p);
01652     }
01653 
01654     PG_RETURN_TEXT_P(cstring_to_text(buf));
01655 }
01656 
01657 /*
01658  * array_lower :
01659  *      returns the lower dimension, of the DIM requested, for
01660  *      the array pointed to by "v", as an int4
01661  */
01662 Datum
01663 array_lower(PG_FUNCTION_ARGS)
01664 {
01665     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
01666     int         reqdim = PG_GETARG_INT32(1);
01667     int        *lb;
01668     int         result;
01669 
01670     /* Sanity check: does it look like an array at all? */
01671     if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
01672         PG_RETURN_NULL();
01673 
01674     /* Sanity check: was the requested dim valid */
01675     if (reqdim <= 0 || reqdim > ARR_NDIM(v))
01676         PG_RETURN_NULL();
01677 
01678     lb = ARR_LBOUND(v);
01679     result = lb[reqdim - 1];
01680 
01681     PG_RETURN_INT32(result);
01682 }
01683 
01684 /*
01685  * array_upper :
01686  *      returns the upper dimension, of the DIM requested, for
01687  *      the array pointed to by "v", as an int4
01688  */
01689 Datum
01690 array_upper(PG_FUNCTION_ARGS)
01691 {
01692     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
01693     int         reqdim = PG_GETARG_INT32(1);
01694     int        *dimv,
01695                *lb;
01696     int         result;
01697 
01698     /* Sanity check: does it look like an array at all? */
01699     if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
01700         PG_RETURN_NULL();
01701 
01702     /* Sanity check: was the requested dim valid */
01703     if (reqdim <= 0 || reqdim > ARR_NDIM(v))
01704         PG_RETURN_NULL();
01705 
01706     lb = ARR_LBOUND(v);
01707     dimv = ARR_DIMS(v);
01708 
01709     result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
01710 
01711     PG_RETURN_INT32(result);
01712 }
01713 
01714 /*
01715  * array_length :
01716  *      returns the length, of the dimension requested, for
01717  *      the array pointed to by "v", as an int4
01718  */
01719 Datum
01720 array_length(PG_FUNCTION_ARGS)
01721 {
01722     ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
01723     int         reqdim = PG_GETARG_INT32(1);
01724     int        *dimv;
01725     int         result;
01726 
01727     /* Sanity check: does it look like an array at all? */
01728     if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
01729         PG_RETURN_NULL();
01730 
01731     /* Sanity check: was the requested dim valid */
01732     if (reqdim <= 0 || reqdim > ARR_NDIM(v))
01733         PG_RETURN_NULL();
01734 
01735     dimv = ARR_DIMS(v);
01736 
01737     result = dimv[reqdim - 1];
01738 
01739     PG_RETURN_INT32(result);
01740 }
01741 
01742 /*
01743  * array_ref :
01744  *    This routine takes an array pointer and a subscript array and returns
01745  *    the referenced item as a Datum.  Note that for a pass-by-reference
01746  *    datatype, the returned Datum is a pointer into the array object.
01747  *
01748  * This handles both ordinary varlena arrays and fixed-length arrays.
01749  *
01750  * Inputs:
01751  *  array: the array object (mustn't be NULL)
01752  *  nSubscripts: number of subscripts supplied
01753  *  indx[]: the subscript values
01754  *  arraytyplen: pg_type.typlen for the array type
01755  *  elmlen: pg_type.typlen for the array's element type
01756  *  elmbyval: pg_type.typbyval for the array's element type
01757  *  elmalign: pg_type.typalign for the array's element type
01758  *
01759  * Outputs:
01760  *  The return value is the element Datum.
01761  *  *isNull is set to indicate whether the element is NULL.
01762  */
01763 Datum
01764 array_ref(ArrayType *array,
01765           int nSubscripts,
01766           int *indx,
01767           int arraytyplen,
01768           int elmlen,
01769           bool elmbyval,
01770           char elmalign,
01771           bool *isNull)
01772 {
01773     int         i,
01774                 ndim,
01775                *dim,
01776                *lb,
01777                 offset,
01778                 fixedDim[1],
01779                 fixedLb[1];
01780     char       *arraydataptr,
01781                *retptr;
01782     bits8      *arraynullsptr;
01783 
01784     if (arraytyplen > 0)
01785     {
01786         /*
01787          * fixed-length arrays -- these are assumed to be 1-d, 0-based
01788          */
01789         ndim = 1;
01790         fixedDim[0] = arraytyplen / elmlen;
01791         fixedLb[0] = 0;
01792         dim = fixedDim;
01793         lb = fixedLb;
01794         arraydataptr = (char *) array;
01795         arraynullsptr = NULL;
01796     }
01797     else
01798     {
01799         /* detoast input array if necessary */
01800         array = DatumGetArrayTypeP(PointerGetDatum(array));
01801 
01802         ndim = ARR_NDIM(array);
01803         dim = ARR_DIMS(array);
01804         lb = ARR_LBOUND(array);
01805         arraydataptr = ARR_DATA_PTR(array);
01806         arraynullsptr = ARR_NULLBITMAP(array);
01807     }
01808 
01809     /*
01810      * Return NULL for invalid subscript
01811      */
01812     if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
01813     {
01814         *isNull = true;
01815         return (Datum) 0;
01816     }
01817     for (i = 0; i < ndim; i++)
01818     {
01819         if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
01820         {
01821             *isNull = true;
01822             return (Datum) 0;
01823         }
01824     }
01825 
01826     /*
01827      * Calculate the element number
01828      */
01829     offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
01830 
01831     /*
01832      * Check for NULL array element
01833      */
01834     if (array_get_isnull(arraynullsptr, offset))
01835     {
01836         *isNull = true;
01837         return (Datum) 0;
01838     }
01839 
01840     /*
01841      * OK, get the element
01842      */
01843     *isNull = false;
01844     retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
01845                         elmlen, elmbyval, elmalign);
01846     return ArrayCast(retptr, elmbyval, elmlen);
01847 }
01848 
01849 /*
01850  * array_get_slice :
01851  *         This routine takes an array and a range of indices (upperIndex and
01852  *         lowerIndx), creates a new array structure for the referred elements
01853  *         and returns a pointer to it.
01854  *
01855  * This handles both ordinary varlena arrays and fixed-length arrays.
01856  *
01857  * Inputs:
01858  *  array: the array object (mustn't be NULL)
01859  *  nSubscripts: number of subscripts supplied (must be same for upper/lower)
01860  *  upperIndx[]: the upper subscript values
01861  *  lowerIndx[]: the lower subscript values
01862  *  arraytyplen: pg_type.typlen for the array type
01863  *  elmlen: pg_type.typlen for the array's element type
01864  *  elmbyval: pg_type.typbyval for the array's element type
01865  *  elmalign: pg_type.typalign for the array's element type
01866  *
01867  * Outputs:
01868  *  The return value is the new array Datum (it's never NULL)
01869  *
01870  * NOTE: we assume it is OK to scribble on the provided subscript arrays
01871  * lowerIndx[] and upperIndx[].  These are generally just temporaries.
01872  */
01873 ArrayType *
01874 array_get_slice(ArrayType *array,
01875                 int nSubscripts,
01876                 int *upperIndx,
01877                 int *lowerIndx,
01878                 int arraytyplen,
01879                 int elmlen,
01880                 bool elmbyval,
01881                 char elmalign)
01882 {
01883     ArrayType  *newarray;
01884     int         i,
01885                 ndim,
01886                *dim,
01887                *lb,
01888                *newlb;
01889     int         fixedDim[1],
01890                 fixedLb[1];
01891     Oid         elemtype;
01892     char       *arraydataptr;
01893     bits8      *arraynullsptr;
01894     int32       dataoffset;
01895     int         bytes,
01896                 span[MAXDIM];
01897 
01898     if (arraytyplen > 0)
01899     {
01900         /*
01901          * fixed-length arrays -- currently, cannot slice these because parser
01902          * labels output as being of the fixed-length array type! Code below
01903          * shows how we could support it if the parser were changed to label
01904          * output as a suitable varlena array type.
01905          */
01906         ereport(ERROR,
01907                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01908                  errmsg("slices of fixed-length arrays not implemented")));
01909 
01910         /*
01911          * fixed-length arrays -- these are assumed to be 1-d, 0-based
01912          *
01913          * XXX where would we get the correct ELEMTYPE from?
01914          */
01915         ndim = 1;
01916         fixedDim[0] = arraytyplen / elmlen;
01917         fixedLb[0] = 0;
01918         dim = fixedDim;
01919         lb = fixedLb;
01920         elemtype = InvalidOid;  /* XXX */
01921         arraydataptr = (char *) array;
01922         arraynullsptr = NULL;
01923     }
01924     else
01925     {
01926         /* detoast input array if necessary */
01927         array = DatumGetArrayTypeP(PointerGetDatum(array));
01928 
01929         ndim = ARR_NDIM(array);
01930         dim = ARR_DIMS(array);
01931         lb = ARR_LBOUND(array);
01932         elemtype = ARR_ELEMTYPE(array);
01933         arraydataptr = ARR_DATA_PTR(array);
01934         arraynullsptr = ARR_NULLBITMAP(array);
01935     }
01936 
01937     /*
01938      * Check provided subscripts.  A slice exceeding the current array limits
01939      * is silently truncated to the array limits.  If we end up with an empty
01940      * slice, return an empty array.
01941      */
01942     if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
01943         return construct_empty_array(elemtype);
01944 
01945     for (i = 0; i < nSubscripts; i++)
01946     {
01947         if (lowerIndx[i] < lb[i])
01948             lowerIndx[i] = lb[i];
01949         if (upperIndx[i] >= (dim[i] + lb[i]))
01950             upperIndx[i] = dim[i] + lb[i] - 1;
01951         if (lowerIndx[i] > upperIndx[i])
01952             return construct_empty_array(elemtype);
01953     }
01954     /* fill any missing subscript positions with full array range */
01955     for (; i < ndim; i++)
01956     {
01957         lowerIndx[i] = lb[i];
01958         upperIndx[i] = dim[i] + lb[i] - 1;
01959         if (lowerIndx[i] > upperIndx[i])
01960             return construct_empty_array(elemtype);
01961     }
01962 
01963     mda_get_range(ndim, span, lowerIndx, upperIndx);
01964 
01965     bytes = array_slice_size(arraydataptr, arraynullsptr,
01966                              ndim, dim, lb,
01967                              lowerIndx, upperIndx,
01968                              elmlen, elmbyval, elmalign);
01969 
01970     /*
01971      * Currently, we put a null bitmap in the result if the source has one;
01972      * could be smarter ...
01973      */
01974     if (arraynullsptr)
01975     {
01976         dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
01977         bytes += dataoffset;
01978     }
01979     else
01980     {
01981         dataoffset = 0;         /* marker for no null bitmap */
01982         bytes += ARR_OVERHEAD_NONULLS(ndim);
01983     }
01984 
01985     newarray = (ArrayType *) palloc0(bytes);
01986     SET_VARSIZE(newarray, bytes);
01987     newarray->ndim = ndim;
01988     newarray->dataoffset = dataoffset;
01989     newarray->elemtype = elemtype;
01990     memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
01991 
01992     /*
01993      * Lower bounds of the new array are set to 1.  Formerly (before 7.3) we
01994      * copied the given lowerIndx values ... but that seems confusing.
01995      */
01996     newlb = ARR_LBOUND(newarray);
01997     for (i = 0; i < ndim; i++)
01998         newlb[i] = 1;
01999 
02000     array_extract_slice(newarray,
02001                         ndim, dim, lb,
02002                         arraydataptr, arraynullsptr,
02003                         lowerIndx, upperIndx,
02004                         elmlen, elmbyval, elmalign);
02005 
02006     return newarray;
02007 }
02008 
02009 /*
02010  * array_set :
02011  *        This routine sets the value of an array element (specified by
02012  *        a subscript array) to a new value specified by "dataValue".
02013  *
02014  * This handles both ordinary varlena arrays and fixed-length arrays.
02015  *
02016  * Inputs:
02017  *  array: the initial array object (mustn't be NULL)
02018  *  nSubscripts: number of subscripts supplied
02019  *  indx[]: the subscript values
02020  *  dataValue: the datum to be inserted at the given position
02021  *  isNull: whether dataValue is NULL
02022  *  arraytyplen: pg_type.typlen for the array type
02023  *  elmlen: pg_type.typlen for the array's element type
02024  *  elmbyval: pg_type.typbyval for the array's element type
02025  *  elmalign: pg_type.typalign for the array's element type
02026  *
02027  * Result:
02028  *        A new array is returned, just like the old except for the one
02029  *        modified entry.  The original array object is not changed.
02030  *
02031  * For one-dimensional arrays only, we allow the array to be extended
02032  * by assigning to a position outside the existing subscript range; any
02033  * positions between the existing elements and the new one are set to NULLs.
02034  * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
02035  *
02036  * NOTE: For assignments, we throw an error for invalid subscripts etc,
02037  * rather than returning a NULL as the fetch operations do.
02038  */
02039 ArrayType *
02040 array_set(ArrayType *array,
02041           int nSubscripts,
02042           int *indx,
02043           Datum dataValue,
02044           bool isNull,
02045           int arraytyplen,
02046           int elmlen,
02047           bool elmbyval,
02048           char elmalign)
02049 {
02050     ArrayType  *newarray;
02051     int         i,
02052                 ndim,
02053                 dim[MAXDIM],
02054                 lb[MAXDIM],
02055                 offset;
02056     char       *elt_ptr;
02057     bool        newhasnulls;
02058     bits8      *oldnullbitmap;
02059     int         oldnitems,
02060                 newnitems,
02061                 olddatasize,
02062                 newsize,
02063                 olditemlen,
02064                 newitemlen,
02065                 overheadlen,
02066                 oldoverheadlen,
02067                 addedbefore,
02068                 addedafter,
02069                 lenbefore,
02070                 lenafter;
02071 
02072     if (arraytyplen > 0)
02073     {
02074         /*
02075          * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
02076          * cannot extend them, either.
02077          */
02078         if (nSubscripts != 1)
02079             ereport(ERROR,
02080                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
02081                      errmsg("wrong number of array subscripts")));
02082 
02083         if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
02084             ereport(ERROR,
02085                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
02086                      errmsg("array subscript out of range")));
02087 
02088         if (isNull)
02089             ereport(ERROR,
02090                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
02091                      errmsg("cannot assign null value to an element of a fixed-length array")));
02092 
02093         newarray = (ArrayType *) palloc(arraytyplen);
02094         memcpy(newarray, array, arraytyplen);
02095         elt_ptr = (char *) newarray + indx[0] * elmlen;
02096         ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
02097         return newarray;
02098     }
02099 
02100     if (nSubscripts <= 0 || nSubscripts > MAXDIM)
02101         ereport(ERROR,
02102                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
02103                  errmsg("wrong number of array subscripts")));
02104 
02105     /* make sure item to be inserted is not toasted */
02106     if (elmlen == -1 && !isNull)
02107         dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
02108 
02109     /* detoast input array if necessary */
02110     array = DatumGetArrayTypeP(PointerGetDatum(array));
02111 
02112     ndim = ARR_NDIM(array);
02113 
02114     /*
02115      * if number of dims is zero, i.e. an empty array, create an array with
02116      * nSubscripts dimensions, and set the lower bounds to the supplied
02117      * subscripts
02118      */
02119     if (ndim == 0)
02120     {
02121         Oid         elmtype = ARR_ELEMTYPE(array);
02122 
02123         for (i = 0; i < nSubscripts; i++)
02124         {
02125             dim[i] = 1;
02126             lb[i] = indx[i];
02127         }
02128 
02129         return construct_md_array(&dataValue, &isNull, nSubscripts,
02130                                   dim, lb, elmtype,
02131                                   elmlen, elmbyval, elmalign);
02132     }
02133 
02134     if (ndim != nSubscripts)
02135         ereport(ERROR,
02136                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
02137                  errmsg("wrong number of array subscripts")));
02138 
02139     /* copy dim/lb since we may modify them */
02140     memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
02141     memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
02142 
02143     newhasnulls = (ARR_HASNULL(array) || isNull);
02144     addedbefore = addedafter = 0;
02145 
02146     /*
02147      * Check subscripts
02148      */
02149     if (ndim == 1)
02150     {
02151         if (indx[0] < lb[0])
02152         {
02153             addedbefore = lb[0] - indx[0];
02154             dim[0] += addedbefore;
02155             lb[0] = indx[0];
02156             if (addedbefore > 1)
02157                 newhasnulls = true;     /* will insert nulls */
02158         }
02159         if (indx[0] >= (dim[0] + lb[0]))
02160         {
02161             addedafter = indx[0] - (dim[0] + lb[0]) + 1;
02162             dim[0] += addedafter;
02163             if (addedafter > 1)
02164                 newhasnulls = true;     /* will insert nulls */
02165         }
02166     }
02167     else
02168     {
02169         /*
02170          * XXX currently we do not support extending multi-dimensional arrays
02171          * during assignment
02172          */
02173         for (i = 0; i < ndim; i++)
02174         {
02175             if (indx[i] < lb[i] ||
02176                 indx[i] >= (dim[i] + lb[i]))
02177                 ereport(ERROR,
02178                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
02179                          errmsg("array subscript out of range")));
02180         }
02181     }
02182 
02183     /*
02184      * Compute sizes of items and areas to copy
02185      */
02186     newnitems = ArrayGetNItems(ndim, dim);
02187     if (newhasnulls)
02188         overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
02189     else
02190         overheadlen = ARR_OVERHEAD_NONULLS(ndim);
02191     oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
02192     oldnullbitmap = ARR_NULLBITMAP(array);
02193     oldoverheadlen = ARR_DATA_OFFSET(array);
02194     olddatasize = ARR_SIZE(array) - oldoverheadlen;
02195     if (addedbefore)
02196     {
02197         offset = 0;
02198         lenbefore = 0;
02199         olditemlen = 0;
02200         lenafter = olddatasize;
02201     }
02202     else if (addedafter)
02203     {
02204         offset = oldnitems;
02205         lenbefore = olddatasize;
02206         olditemlen = 0;
02207         lenafter = 0;
02208     }
02209     else
02210     {
02211         offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
02212         elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
02213                              elmlen, elmbyval, elmalign);
02214         lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
02215         if (array_get_isnull(oldnullbitmap, offset))
02216             olditemlen = 0;
02217         else
02218         {
02219             olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
02220             olditemlen = att_align_nominal(olditemlen, elmalign);
02221         }
02222         lenafter = (int) (olddatasize - lenbefore - olditemlen);
02223     }
02224 
02225     if (isNull)
02226         newitemlen = 0;
02227     else
02228     {
02229         newitemlen = att_addlength_datum(0, elmlen, dataValue);
02230         newitemlen = att_align_nominal(newitemlen, elmalign);
02231     }
02232 
02233     newsize = overheadlen + lenbefore + newitemlen + lenafter;
02234 
02235     /*
02236      * OK, create the new array and fill in header/dimensions
02237      */
02238     newarray = (ArrayType *) palloc0(newsize);
02239     SET_VARSIZE(newarray, newsize);
02240     newarray->ndim = ndim;
02241     newarray->dataoffset = newhasnulls ? overheadlen : 0;
02242     newarray->elemtype = ARR_ELEMTYPE(array);
02243     memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
02244     memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
02245 
02246     /*
02247      * Fill in data
02248      */
02249     memcpy((char *) newarray + overheadlen,
02250            (char *) array + oldoverheadlen,
02251            lenbefore);
02252     if (!isNull)
02253         ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
02254                         (char *) newarray + overheadlen + lenbefore);
02255     memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
02256            (char *) array + oldoverheadlen + lenbefore + olditemlen,
02257            lenafter);
02258 
02259     /*
02260      * Fill in nulls bitmap if needed
02261      *
02262      * Note: it's possible we just replaced the last NULL with a non-NULL, and
02263      * could get rid of the bitmap.  Seems not worth testing for though.
02264      */
02265     if (newhasnulls)
02266     {
02267         bits8      *newnullbitmap = ARR_NULLBITMAP(newarray);
02268 
02269         /* Zero the bitmap to take care of marking inserted positions null */
02270         MemSet(newnullbitmap, 0, (newnitems + 7) / 8);
02271         /* Fix the inserted value */
02272         if (addedafter)
02273             array_set_isnull(newnullbitmap, newnitems - 1, isNull);
02274         else
02275             array_set_isnull(newnullbitmap, offset, isNull);
02276         /* Fix the copied range(s) */
02277         if (addedbefore)
02278             array_bitmap_copy(newnullbitmap, addedbefore,
02279                               oldnullbitmap, 0,
02280                               oldnitems);
02281         else
02282         {
02283             array_bitmap_copy(newnullbitmap, 0,
02284                               oldnullbitmap, 0,
02285                               offset);
02286             if (addedafter == 0)
02287                 array_bitmap_copy(newnullbitmap, offset + 1,
02288                                   oldnullbitmap, offset + 1,
02289                                   oldnitems - offset - 1);
02290         }
02291     }
02292 
02293     return newarray;
02294 }
02295 
02296 /*
02297  * array_set_slice :
02298  *        This routine sets the value of a range of array locations (specified
02299  *        by upper and lower subscript values) to new values passed as
02300  *        another array.
02301  *
02302  * This handles both ordinary varlena arrays and fixed-length arrays.
02303  *
02304  * Inputs:
02305  *  array: the initial array object (mustn't be NULL)
02306  *  nSubscripts: number of subscripts supplied (must be same for upper/lower)
02307  *  upperIndx[]: the upper subscript values
02308  *  lowerIndx[]: the lower subscript values
02309  *  srcArray: the source for the inserted values
02310  *  isNull: indicates whether srcArray is NULL
02311  *  arraytyplen: pg_type.typlen for the array type
02312  *  elmlen: pg_type.typlen for the array's element type
02313  *  elmbyval: pg_type.typbyval for the array's element type
02314  *  elmalign: pg_type.typalign for the array's element type
02315  *
02316  * Result:
02317  *        A new array is returned, just like the old except for the
02318  *        modified range.  The original array object is not changed.
02319  *
02320  * For one-dimensional arrays only, we allow the array to be extended
02321  * by assigning to positions outside the existing subscript range; any
02322  * positions between the existing elements and the new ones are set to NULLs.
02323  * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
02324  *
02325  * NOTE: we assume it is OK to scribble on the provided index arrays
02326  * lowerIndx[] and upperIndx[].  These are generally just temporaries.
02327  *
02328  * NOTE: For assignments, we throw an error for silly subscripts etc,
02329  * rather than returning a NULL or empty array as the fetch operations do.
02330  */
02331 ArrayType *
02332 array_set_slice(ArrayType *array,
02333                 int nSubscripts,
02334                 int *upperIndx,
02335                 int *lowerIndx,
02336                 ArrayType *srcArray,
02337                 bool isNull,
02338                 int arraytyplen,
02339                 int elmlen,
02340                 bool elmbyval,
02341                 char elmalign)
02342 {
02343     ArrayType  *newarray;
02344     int         i,
02345                 ndim,
02346                 dim[MAXDIM],
02347                 lb[MAXDIM],
02348                 span[MAXDIM];
02349     bool        newhasnulls;
02350     int         nitems,
02351                 nsrcitems,
02352                 olddatasize,
02353                 newsize,
02354                 olditemsize,
02355                 newitemsize,
02356                 overheadlen,
02357                 oldoverheadlen,
02358                 addedbefore,
02359                 addedafter,
02360                 lenbefore,
02361                 lenafter,
02362                 itemsbefore,
02363                 itemsafter,
02364                 nolditems;
02365 
02366     /* Currently, assignment from a NULL source array is a no-op */
02367     if (isNull)
02368         return array;
02369 
02370     if (arraytyplen > 0)
02371     {
02372         /*
02373          * fixed-length arrays -- not got round to doing this...
02374          */
02375         ereport(ERROR,
02376                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
02377         errmsg("updates on slices of fixed-length arrays not implemented")));
02378     }
02379 
02380     /* detoast arrays if necessary */
02381     array = DatumGetArrayTypeP(PointerGetDatum(array));
02382     srcArray = DatumGetArrayTypeP(PointerGetDatum(srcArray));
02383 
02384     /* note: we assume srcArray contains no toasted elements */
02385 
02386     ndim = ARR_NDIM(array);
02387 
02388     /*
02389      * if number of dims is zero, i.e. an empty array, create an array with
02390      * nSubscripts dimensions, and set the upper and lower bounds to the
02391      * supplied subscripts
02392      */
02393     if (ndim == 0)
02394     {
02395         Datum      *dvalues;
02396         bool       *dnulls;
02397         int         nelems;
02398         Oid         elmtype = ARR_ELEMTYPE(array);
02399 
02400         deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
02401                           &dvalues, &dnulls, &nelems);
02402 
02403         for (i = 0; i < nSubscripts; i++)
02404         {
02405             dim[i] = 1 + upperIndx[i] - lowerIndx[i];
02406             lb[i] = lowerIndx[i];
02407         }
02408 
02409         /* complain if too few source items; we ignore extras, however */
02410         if (nelems < ArrayGetNItems(nSubscripts, dim))
02411             ereport(ERROR,
02412                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
02413                      errmsg("source array too small")));
02414 
02415         return construct_md_array(dvalues, dnulls, nSubscripts,
02416                                   dim, lb, elmtype,
02417                                   elmlen, elmbyval, elmalign);
02418     }
02419 
02420     if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
02421         ereport(ERROR,
02422                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
02423                  errmsg("wrong number of array subscripts")));
02424 
02425     /* copy dim/lb since we may modify them */
02426     memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
02427     memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
02428 
02429     newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
02430     addedbefore = addedafter = 0;
02431 
02432     /*
02433      * Check subscripts
02434      */
02435     if (ndim == 1)
02436     {
02437         Assert(nSubscripts == 1);
02438         if (lowerIndx[0] > upperIndx[0])
02439             ereport(ERROR,
02440                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
02441                      errmsg("upper bound cannot be less than lower bound")));
02442         if (lowerIndx[0] < lb[0])
02443         {
02444             if (upperIndx[0] < lb[0] - 1)
02445                 newhasnulls = true;     /* will insert nulls */
02446             addedbefore = lb[0] - lowerIndx[0];
02447             dim[0] += addedbefore;
02448             lb[0] = lowerIndx[0];
02449         }
02450         if (upperIndx[0] >= (dim[0] + lb[0]))
02451         {
02452             if (lowerIndx[0] > (dim[0] + lb[0]))
02453                 newhasnulls = true;     /* will insert nulls */
02454             addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1;
02455             dim[0] += addedafter;
02456         }
02457     }
02458     else
02459     {
02460         /*
02461          * XXX currently we do not support extending multi-dimensional arrays
02462          * during assignment
02463          */
02464         for (i = 0; i < nSubscripts; i++)
02465         {
02466             if (lowerIndx[i] > upperIndx[i])
02467                 ereport(ERROR,
02468                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
02469                      errmsg("upper bound cannot be less than lower bound")));
02470             if (lowerIndx[i] < lb[i] ||
02471                 upperIndx[i] >= (dim[i] + lb[i]))
02472                 ereport(ERROR,
02473                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
02474                          errmsg("array subscript out of range")));
02475         }
02476         /* fill any missing subscript positions with full array range */
02477         for (; i < ndim; i++)
02478         {
02479             lowerIndx[i] = lb[i];
02480             upperIndx[i] = dim[i] + lb[i] - 1;
02481             if (lowerIndx[i] > upperIndx[i])
02482                 ereport(ERROR,
02483                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
02484                      errmsg("upper bound cannot be less than lower bound")));
02485         }
02486     }
02487 
02488     /* Do this mainly to check for overflow */
02489     nitems = ArrayGetNItems(ndim, dim);
02490 
02491     /*
02492      * Make sure source array has enough entries.  Note we ignore the shape of
02493      * the source array and just read entries serially.
02494      */
02495     mda_get_range(ndim, span, lowerIndx, upperIndx);
02496     nsrcitems = ArrayGetNItems(ndim, span);
02497     if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
02498         ereport(ERROR,
02499                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
02500                  errmsg("source array too small")));
02501 
02502     /*
02503      * Compute space occupied by new entries, space occupied by replaced
02504      * entries, and required space for new array.
02505      */
02506     if (newhasnulls)
02507         overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
02508     else
02509         overheadlen = ARR_OVERHEAD_NONULLS(ndim);
02510     newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
02511                                     ARR_NULLBITMAP(srcArray), nsrcitems,
02512                                     elmlen, elmbyval, elmalign);
02513     oldoverheadlen = ARR_DATA_OFFSET(array);
02514     olddatasize = ARR_SIZE(array) - oldoverheadlen;
02515     if (ndim > 1)
02516     {
02517         /*
02518          * here we do not need to cope with extension of the array; it would
02519          * be a lot more complicated if we had to do so...
02520          */
02521         olditemsize = array_slice_size(ARR_DATA_PTR(array),
02522                                        ARR_NULLBITMAP(array),
02523                                        ndim, dim, lb,
02524                                        lowerIndx, upperIndx,
02525                                        elmlen, elmbyval, elmalign);
02526         lenbefore = lenafter = 0;       /* keep compiler quiet */
02527         itemsbefore = itemsafter = nolditems = 0;
02528     }
02529     else
02530     {
02531         /*
02532          * here we must allow for possibility of slice larger than orig array
02533          * and/or not adjacent to orig array subscripts
02534          */
02535         int         oldlb = ARR_LBOUND(array)[0];
02536         int         oldub = oldlb + ARR_DIMS(array)[0] - 1;
02537         int         slicelb = Max(oldlb, lowerIndx[0]);
02538         int         sliceub = Min(oldub, upperIndx[0]);
02539         char       *oldarraydata = ARR_DATA_PTR(array);
02540         bits8      *oldarraybitmap = ARR_NULLBITMAP(array);
02541 
02542         /* count/size of old array entries that will go before the slice */
02543         itemsbefore = Min(slicelb, oldub + 1) - oldlb;
02544         lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
02545                                       itemsbefore,
02546                                       elmlen, elmbyval, elmalign);
02547         /* count/size of old array entries that will be replaced by slice */
02548         if (slicelb > sliceub)
02549         {
02550             nolditems = 0;
02551             olditemsize = 0;
02552         }
02553         else
02554         {
02555             nolditems = sliceub - slicelb + 1;
02556             olditemsize = array_nelems_size(oldarraydata + lenbefore,
02557                                             itemsbefore, oldarraybitmap,
02558                                             nolditems,
02559                                             elmlen, elmbyval, elmalign);
02560         }
02561         /* count/size of old array entries that will go after the slice */
02562         itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
02563         lenafter = olddatasize - lenbefore - olditemsize;
02564     }
02565 
02566     newsize = overheadlen + olddatasize - olditemsize + newitemsize;
02567 
02568     newarray = (ArrayType *) palloc0(newsize);
02569     SET_VARSIZE(newarray, newsize);
02570     newarray->ndim = ndim;
02571     newarray->dataoffset = newhasnulls ? overheadlen : 0;
02572     newarray->elemtype = ARR_ELEMTYPE(array);
02573     memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
02574     memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
02575 
02576     if (ndim > 1)
02577     {
02578         /*
02579          * here we do not need to cope with extension of the array; it would
02580          * be a lot more complicated if we had to do so...
02581          */
02582         array_insert_slice(newarray, array, srcArray,
02583                            ndim, dim, lb,
02584                            lowerIndx, upperIndx,
02585                            elmlen, elmbyval, elmalign);
02586     }
02587     else
02588     {
02589         /* fill in data */
02590         memcpy((char *) newarray + overheadlen,
02591                (char *) array + oldoverheadlen,
02592                lenbefore);
02593         memcpy((char *) newarray + overheadlen + lenbefore,
02594                ARR_DATA_PTR(srcArray),
02595                newitemsize);
02596         memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
02597                (char *) array + oldoverheadlen + lenbefore + olditemsize,
02598                lenafter);
02599         /* fill in nulls bitmap if needed */
02600         if (newhasnulls)
02601         {
02602             bits8      *newnullbitmap = ARR_NULLBITMAP(newarray);
02603             bits8      *oldnullbitmap = ARR_NULLBITMAP(array);
02604 
02605             /* Zero the bitmap to handle marking inserted positions null */
02606             MemSet(newnullbitmap, 0, (nitems + 7) / 8);
02607             array_bitmap_copy(newnullbitmap, addedbefore,
02608                               oldnullbitmap, 0,
02609                               itemsbefore);
02610             array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
02611                               ARR_NULLBITMAP(srcArray), 0,
02612                               nsrcitems);
02613             array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
02614                               oldnullbitmap, itemsbefore + nolditems,
02615                               itemsafter);
02616         }
02617     }
02618 
02619     return newarray;
02620 }
02621 
02622 /*
02623  * array_map()
02624  *
02625  * Map an array through an arbitrary function.  Return a new array with
02626  * same dimensions and each source element transformed by fn().  Each
02627  * source element is passed as the first argument to fn(); additional
02628  * arguments to be passed to fn() can be specified by the caller.
02629  * The output array can have a different element type than the input.
02630  *
02631  * Parameters are:
02632  * * fcinfo: a function-call data structure pre-constructed by the caller
02633  *   to be ready to call the desired function, with everything except the
02634  *   first argument position filled in.  In particular, flinfo identifies
02635  *   the function fn(), and if nargs > 1 then argument positions after the
02636  *   first must be preset to the additional values to be passed.  The
02637  *   first argument position initially holds the input array value.
02638  * * inpType: OID of element type of input array.  This must be the same as,
02639  *   or binary-compatible with, the first argument type of fn().
02640  * * retType: OID of element type of output array.  This must be the same as,
02641  *   or binary-compatible with, the result type of fn().
02642  * * amstate: workspace for array_map.  Must be zeroed by caller before
02643  *   first call, and not touched after that.
02644  *
02645  * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
02646  * but better performance can be had if the state can be preserved across
02647  * a series of calls.
02648  *
02649  * NB: caller must assure that input array is not NULL.  NULL elements in
02650  * the array are OK however.
02651  */
02652 Datum
02653 array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
02654           ArrayMapState *amstate)
02655 {
02656     ArrayType  *v;
02657     ArrayType  *result;
02658     Datum      *values;
02659     bool       *nulls;
02660     Datum       elt;
02661     int        *dim;
02662     int         ndim;
02663     int         nitems;
02664     int         i;
02665     int32       nbytes = 0;
02666     int32       dataoffset;
02667     bool        hasnulls;
02668     int         inp_typlen;
02669     bool        inp_typbyval;
02670     char        inp_typalign;
02671     int         typlen;
02672     bool        typbyval;
02673     char        typalign;
02674     char       *s;
02675     bits8      *bitmap;
02676     int         bitmask;
02677     ArrayMetaState *inp_extra;
02678     ArrayMetaState *ret_extra;
02679 
02680     /* Get input array */
02681     if (fcinfo->nargs < 1)
02682         elog(ERROR, "invalid nargs: %d", fcinfo->nargs);
02683     if (PG_ARGISNULL(0))
02684         elog(ERROR, "null input array");
02685     v = PG_GETARG_ARRAYTYPE_P(0);
02686 
02687     Assert(ARR_ELEMTYPE(v) == inpType);
02688 
02689     ndim = ARR_NDIM(v);
02690     dim = ARR_DIMS(v);
02691     nitems = ArrayGetNItems(ndim, dim);
02692 
02693     /* Check for empty array */
02694     if (nitems <= 0)
02695     {
02696         /* Return empty array */
02697         PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType));
02698     }
02699 
02700     /*
02701      * We arrange to look up info about input and return element types only
02702      * once per series of calls, assuming the element type doesn't change
02703      * underneath us.
02704      */
02705     inp_extra = &amstate->inp_extra;
02706     ret_extra = &amstate->ret_extra;
02707 
02708     if (inp_extra->element_type != inpType)
02709     {
02710         get_typlenbyvalalign(inpType,
02711                              &inp_extra->typlen,
02712                              &inp_extra->typbyval,
02713                              &inp_extra->typalign);
02714         inp_extra->element_type = inpType;
02715     }
02716     inp_typlen = inp_extra->typlen;
02717     inp_typbyval = inp_extra->typbyval;
02718     inp_typalign = inp_extra->typalign;
02719 
02720     if (ret_extra->element_type != retType)
02721     {
02722         get_typlenbyvalalign(retType,
02723                              &ret_extra->typlen,
02724                              &ret_extra->typbyval,
02725                              &ret_extra->typalign);
02726         ret_extra->element_type = retType;
02727     }
02728     typlen = ret_extra->typlen;
02729     typbyval = ret_extra->typbyval;
02730     typalign = ret_extra->typalign;
02731 
02732     /* Allocate temporary arrays for new values */
02733     values = (Datum *) palloc(nitems * sizeof(Datum));
02734     nulls = (bool *) palloc(nitems * sizeof(bool));
02735 
02736     /* Loop over source data */
02737     s = ARR_DATA_PTR(v);
02738     bitmap = ARR_NULLBITMAP(v);
02739     bitmask = 1;
02740     hasnulls = false;
02741 
02742     for (i = 0; i < nitems; i++)
02743     {
02744         bool        callit = true;
02745 
02746         /* Get source element, checking for NULL */
02747         if (bitmap && (*bitmap & bitmask) == 0)
02748         {
02749             fcinfo->argnull[0] = true;
02750         }
02751         else
02752         {
02753             elt = fetch_att(s, inp_typbyval, inp_typlen);
02754             s = att_addlength_datum(s, inp_typlen, elt);
02755             s = (char *) att_align_nominal(s, inp_typalign);
02756             fcinfo->arg[0] = elt;
02757             fcinfo->argnull[0] = false;
02758         }
02759 
02760         /*
02761          * Apply the given function to source elt and extra args.
02762          */
02763         if (fcinfo->flinfo->fn_strict)
02764         {
02765             int         j;
02766 
02767             for (j = 0; j < fcinfo->nargs; j++)
02768             {
02769                 if (fcinfo->argnull[j])
02770                 {
02771                     callit = false;
02772                     break;
02773                 }
02774             }
02775         }
02776 
02777         if (callit)
02778         {
02779             fcinfo->isnull = false;
02780             values[i] = FunctionCallInvoke(fcinfo);
02781         }
02782         else
02783             fcinfo->isnull = true;
02784 
02785         nulls[i] = fcinfo->isnull;
02786         if (fcinfo->isnull)
02787             hasnulls = true;
02788         else
02789         {
02790             /* Ensure data is not toasted */
02791             if (typlen == -1)
02792                 values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
02793             /* Update total result size */
02794             nbytes = att_addlength_datum(nbytes, typlen, values[i]);
02795             nbytes = att_align_nominal(nbytes, typalign);
02796             /* check for overflow of total request */
02797             if (!AllocSizeIsValid(nbytes))
02798                 ereport(ERROR,
02799                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
02800                          errmsg("array size exceeds the maximum allowed (%d)",
02801                                 (int) MaxAllocSize)));
02802         }
02803 
02804         /* advance bitmap pointer if any */
02805         if (bitmap)
02806         {
02807             bitmask <<= 1;
02808             if (bitmask == 0x100)
02809             {
02810                 bitmap++;
02811                 bitmask = 1;
02812             }
02813         }
02814     }
02815 
02816     /* Allocate and initialize the result array */
02817     if (hasnulls)
02818     {
02819         dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
02820         nbytes += dataoffset;
02821     }
02822     else
02823     {
02824         dataoffset = 0;         /* marker for no null bitmap */
02825         nbytes += ARR_OVERHEAD_NONULLS(ndim);
02826     }
02827     result = (ArrayType *) palloc0(nbytes);
02828     SET_VARSIZE(result, nbytes);
02829     result->ndim = ndim;
02830     result->dataoffset = dataoffset;
02831     result->elemtype = retType;
02832     memcpy(ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int));
02833 
02834     /*
02835      * Note: do not risk trying to pfree the results of the called function
02836      */
02837     CopyArrayEls(result,
02838                  values, nulls, nitems,
02839                  typlen, typbyval, typalign,
02840                  false);
02841 
02842     pfree(values);
02843     pfree(nulls);
02844 
02845     PG_RETURN_ARRAYTYPE_P(result);
02846 }
02847 
02848 /*
02849  * construct_array  --- simple method for constructing an array object
02850  *
02851  * elems: array of Datum items to become the array contents
02852  *        (NULL element values are not supported).
02853  * nelems: number of items
02854  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
02855  *
02856  * A palloc'd 1-D array object is constructed and returned.  Note that
02857  * elem values will be copied into the object even if pass-by-ref type.
02858  *
02859  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
02860  * from the system catalogs, given the elmtype.  However, the caller is
02861  * in a better position to cache this info across multiple uses, or even
02862  * to hard-wire values if the element type is hard-wired.
02863  */
02864 ArrayType *
02865 construct_array(Datum *elems, int nelems,
02866                 Oid elmtype,
02867                 int elmlen, bool elmbyval, char elmalign)
02868 {
02869     int         dims[1];
02870     int         lbs[1];
02871 
02872     dims[0] = nelems;
02873     lbs[0] = 1;
02874 
02875     return construct_md_array(elems, NULL, 1, dims, lbs,
02876                               elmtype, elmlen, elmbyval, elmalign);
02877 }
02878 
02879 /*
02880  * construct_md_array   --- simple method for constructing an array object
02881  *                          with arbitrary dimensions and possible NULLs
02882  *
02883  * elems: array of Datum items to become the array contents
02884  * nulls: array of is-null flags (can be NULL if no nulls)
02885  * ndims: number of dimensions
02886  * dims: integer array with size of each dimension
02887  * lbs: integer array with lower bound of each dimension
02888  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
02889  *
02890  * A palloc'd ndims-D array object is constructed and returned.  Note that
02891  * elem values will be copied into the object even if pass-by-ref type.
02892  *
02893  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
02894  * from the system catalogs, given the elmtype.  However, the caller is
02895  * in a better position to cache this info across multiple uses, or even
02896  * to hard-wire values if the element type is hard-wired.
02897  */
02898 ArrayType *
02899 construct_md_array(Datum *elems,
02900                    bool *nulls,
02901                    int ndims,
02902                    int *dims,
02903                    int *lbs,
02904                    Oid elmtype, int elmlen, bool elmbyval, char elmalign)
02905 {
02906     ArrayType  *result;
02907     bool        hasnulls;
02908     int32       nbytes;
02909     int32       dataoffset;
02910     int         i;
02911     int         nelems;
02912 
02913     if (ndims < 0)              /* we do allow zero-dimension arrays */
02914         ereport(ERROR,
02915                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
02916                  errmsg("invalid number of dimensions: %d", ndims)));
02917     if (ndims > MAXDIM)
02918         ereport(ERROR,
02919                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
02920                  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
02921                         ndims, MAXDIM)));
02922 
02923     /* fast track for empty array */
02924     if (ndims == 0)
02925         return construct_empty_array(elmtype);
02926 
02927     nelems = ArrayGetNItems(ndims, dims);
02928 
02929     /* compute required space */
02930     nbytes = 0;
02931     hasnulls = false;
02932     for (i = 0; i < nelems; i++)
02933     {
02934         if (nulls && nulls[i])
02935         {
02936             hasnulls = true;
02937             continue;
02938         }
02939         /* make sure data is not toasted */
02940         if (elmlen == -1)
02941             elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
02942         nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
02943         nbytes = att_align_nominal(nbytes, elmalign);
02944         /* check for overflow of total request */
02945         if (!AllocSizeIsValid(nbytes))
02946             ereport(ERROR,
02947                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
02948                      errmsg("array size exceeds the maximum allowed (%d)",
02949                             (int) MaxAllocSize)));
02950     }
02951 
02952     /* Allocate and initialize result array */
02953     if (hasnulls)
02954     {
02955         dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
02956         nbytes += dataoffset;
02957     }
02958     else
02959     {
02960         dataoffset = 0;         /* marker for no null bitmap */
02961         nbytes += ARR_OVERHEAD_NONULLS(ndims);
02962     }
02963     result = (ArrayType *) palloc0(nbytes);
02964     SET_VARSIZE(result, nbytes);
02965     result->ndim = ndims;
02966     result->dataoffset = dataoffset;
02967     result->elemtype = elmtype;
02968     memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
02969     memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
02970 
02971     CopyArrayEls(result,
02972                  elems, nulls, nelems,
02973                  elmlen, elmbyval, elmalign,
02974                  false);
02975 
02976     return result;
02977 }
02978 
02979 /*
02980  * construct_empty_array    --- make a zero-dimensional array of given type
02981  */
02982 ArrayType *
02983 construct_empty_array(Oid elmtype)
02984 {
02985     ArrayType  *result;
02986 
02987     result = (ArrayType *) palloc0(sizeof(ArrayType));
02988     SET_VARSIZE(result, sizeof(ArrayType));
02989     result->ndim = 0;
02990     result->dataoffset = 0;
02991     result->elemtype = elmtype;
02992     return result;
02993 }
02994 
02995 /*
02996  * deconstruct_array  --- simple method for extracting data from an array
02997  *
02998  * array: array object to examine (must not be NULL)
02999  * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
03000  * elemsp: return value, set to point to palloc'd array of Datum values
03001  * nullsp: return value, set to point to palloc'd array of isnull markers
03002  * nelemsp: return value, set to number of extracted values
03003  *
03004  * The caller may pass nullsp == NULL if it does not support NULLs in the
03005  * array.  Note that this produces a very uninformative error message,
03006  * so do it only in cases where a NULL is really not expected.
03007  *
03008  * If array elements are pass-by-ref data type, the returned Datums will
03009  * be pointers into the array object.
03010  *
03011  * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
03012  * from the system catalogs, given the elmtype.  However, in most current
03013  * uses the type is hard-wired into the caller and so we can save a lookup
03014  * cycle by hard-wiring the type info as well.
03015  */
03016 void
03017 deconstruct_array(ArrayType *array,
03018                   Oid elmtype,
03019                   int elmlen, bool elmbyval, char elmalign,
03020                   Datum **elemsp, bool **nullsp, int *nelemsp)
03021 {
03022     Datum      *elems;
03023     bool       *nulls;
03024     int         nelems;
03025     char       *p;
03026     bits8      *bitmap;
03027     int         bitmask;
03028     int         i;
03029 
03030     Assert(ARR_ELEMTYPE(array) == elmtype);
03031 
03032     nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
03033     *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
03034     if (nullsp)
03035         *nullsp = nulls = (bool *) palloc0(nelems * sizeof(bool));
03036     else
03037         nulls = NULL;
03038     *nelemsp = nelems;
03039 
03040     p = ARR_DATA_PTR(array);
03041     bitmap = ARR_NULLBITMAP(array);
03042     bitmask = 1;
03043 
03044     for (i = 0; i < nelems; i++)
03045     {
03046         /* Get source element, checking for NULL */
03047         if (bitmap && (*bitmap & bitmask) == 0)
03048         {
03049             elems[i] = (Datum) 0;
03050             if (nulls)
03051                 nulls[i] = true;
03052             else
03053                 ereport(ERROR,
03054                         (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
03055                   errmsg("null array element not allowed in this context")));
03056         }
03057         else
03058         {
03059             elems[i] = fetch_att(p, elmbyval, elmlen);
03060             p = att_addlength_pointer(p, elmlen, p);
03061             p = (char *) att_align_nominal(p, elmalign);
03062         }
03063 
03064         /* advance bitmap pointer if any */
03065         if (bitmap)
03066         {
03067             bitmask <<= 1;
03068             if (bitmask == 0x100)
03069             {
03070                 bitmap++;
03071                 bitmask = 1;
03072             }
03073         }
03074     }
03075 }
03076 
03077 /*
03078  * array_contains_nulls --- detect whether an array has any null elements
03079  *
03080  * This gives an accurate answer, whereas testing ARR_HASNULL only tells
03081  * if the array *might* contain a null.
03082  */
03083 bool
03084 array_contains_nulls(ArrayType *array)
03085 {
03086     int         nelems;
03087     bits8      *bitmap;
03088     int         bitmask;
03089 
03090     /* Easy answer if there's no null bitmap */
03091     if (!ARR_HASNULL(array))
03092         return false;
03093 
03094     nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
03095 
03096     bitmap = ARR_NULLBITMAP(array);
03097 
03098     /* check whole bytes of the bitmap byte-at-a-time */
03099     while (nelems >= 8)
03100     {
03101         if (*bitmap != 0xFF)
03102             return true;
03103         bitmap++;
03104         nelems -= 8;
03105     }
03106 
03107     /* check last partial byte */
03108     bitmask = 1;
03109     while (nelems > 0)
03110     {
03111         if ((*bitmap & bitmask) == 0)
03112             return true;
03113         bitmask <<= 1;
03114         nelems--;
03115     }
03116 
03117     return false;
03118 }
03119 
03120 
03121 /*
03122  * array_eq :
03123  *        compares two arrays for equality
03124  * result :
03125  *        returns true if the arrays are equal, false otherwise.
03126  *
03127  * Note: we do not use array_cmp here, since equality may be meaningful in
03128  * datatypes that don't have a total ordering (and hence no btree support).
03129  */
03130 Datum
03131 array_eq(PG_FUNCTION_ARGS)
03132 {
03133     ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
03134     ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
03135     Oid         collation = PG_GET_COLLATION();
03136     int         ndims1 = ARR_NDIM(array1);
03137     int         ndims2 = ARR_NDIM(array2);
03138     int        *dims1 = ARR_DIMS(array1);
03139     int        *dims2 = ARR_DIMS(array2);
03140     Oid         element_type = ARR_ELEMTYPE(array1);
03141     bool        result = true;
03142     int         nitems;
03143     TypeCacheEntry *typentry;
03144     int         typlen;
03145     bool        typbyval;
03146     char        typalign;
03147     char       *ptr1;
03148     char       *ptr2;
03149     bits8      *bitmap1;
03150     bits8      *bitmap2;
03151     int         bitmask;
03152     int         i;
03153     FunctionCallInfoData locfcinfo;
03154 
03155     if (element_type != ARR_ELEMTYPE(array2))
03156         ereport(ERROR,
03157                 (errcode(ERRCODE_DATATYPE_MISMATCH),
03158                  errmsg("cannot compare arrays of different element types")));
03159 
03160     /* fast path if the arrays do not have the same dimensionality */
03161     if (ndims1 != ndims2 ||
03162         memcmp(dims1, dims2, 2 * ndims1 * sizeof(int)) != 0)
03163         result = false;
03164     else
03165     {
03166         /*
03167          * We arrange to look up the equality function only once per series of
03168          * calls, assuming the element type doesn't change underneath us.  The
03169          * typcache is used so that we have no memory leakage when being used
03170          * as an index support function.
03171          */
03172         typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
03173         if (typentry == NULL ||
03174             typentry->type_id != element_type)
03175         {
03176             typentry = lookup_type_cache(element_type,
03177                                          TYPECACHE_EQ_OPR_FINFO);
03178             if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
03179                 ereport(ERROR,
03180                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
03181                 errmsg("could not identify an equality operator for type %s",
03182                        format_type_be(element_type))));
03183             fcinfo->flinfo->fn_extra = (void *) typentry;
03184         }
03185         typlen = typentry->typlen;
03186         typbyval = typentry->typbyval;
03187         typalign = typentry->typalign;
03188 
03189         /*
03190          * apply the operator to each pair of array elements.
03191          */
03192         InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
03193                                  collation, NULL, NULL);
03194 
03195         /* Loop over source data */
03196         nitems = ArrayGetNItems(ndims1, dims1);
03197         ptr1 = ARR_DATA_PTR(array1);
03198         ptr2 = ARR_DATA_PTR(array2);
03199         bitmap1 = ARR_NULLBITMAP(array1);
03200         bitmap2 = ARR_NULLBITMAP(array2);
03201         bitmask = 1;            /* use same bitmask for both arrays */
03202 
03203         for (i = 0; i < nitems; i++)
03204         {
03205             Datum       elt1;
03206             Datum       elt2;
03207             bool        isnull1;
03208             bool        isnull2;
03209             bool        oprresult;
03210 
03211             /* Get elements, checking for NULL */
03212             if (bitmap1 && (*bitmap1 & bitmask) == 0)
03213             {
03214                 isnull1 = true;
03215                 elt1 = (Datum) 0;
03216             }
03217             else
03218             {
03219                 isnull1 = false;
03220                 elt1 = fetch_att(ptr1, typbyval, typlen);
03221                 ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
03222                 ptr1 = (char *) att_align_nominal(ptr1, typalign);
03223             }
03224 
03225             if (bitmap2 && (*bitmap2 & bitmask) == 0)
03226             {
03227                 isnull2 = true;
03228                 elt2 = (Datum) 0;
03229             }
03230             else
03231             {
03232                 isnull2 = false;
03233                 elt2 = fetch_att(ptr2, typbyval, typlen);
03234                 ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
03235                 ptr2 = (char *) att_align_nominal(ptr2, typalign);
03236             }
03237 
03238             /* advance bitmap pointers if any */
03239             bitmask <<= 1;
03240             if (bitmask == 0x100)
03241             {
03242                 if (bitmap1)
03243                     bitmap1++;
03244                 if (bitmap2)
03245                     bitmap2++;
03246                 bitmask = 1;
03247             }
03248 
03249             /*
03250              * We consider two NULLs equal; NULL and not-NULL are unequal.
03251              */
03252             if (isnull1 && isnull2)
03253                 continue;
03254             if (isnull1 || isnull2)
03255             {
03256                 result = false;
03257                 break;
03258             }
03259 
03260             /*
03261              * Apply the operator to the element pair
03262              */
03263             locfcinfo.arg[0] = elt1;
03264             locfcinfo.arg[1] = elt2;
03265             locfcinfo.argnull[0] = false;
03266             locfcinfo.argnull[1] = false;
03267             locfcinfo.isnull = false;
03268             oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
03269             if (!oprresult)
03270             {
03271                 result = false;
03272                 break;
03273             }
03274         }
03275     }
03276 
03277     /* Avoid leaking memory when handed toasted input. */
03278     PG_FREE_IF_COPY(array1, 0);
03279     PG_FREE_IF_COPY(array2, 1);
03280 
03281     PG_RETURN_BOOL(result);
03282 }
03283 
03284 
03285 /*-----------------------------------------------------------------------------
03286  * array-array bool operators:
03287  *      Given two arrays, iterate comparison operators
03288  *      over the array. Uses logic similar to text comparison
03289  *      functions, except element-by-element instead of
03290  *      character-by-character.
03291  *----------------------------------------------------------------------------
03292  */
03293 
03294 Datum
03295 array_ne(PG_FUNCTION_ARGS)
03296 {
03297     PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
03298 }
03299 
03300 Datum
03301 array_lt(PG_FUNCTION_ARGS)
03302 {
03303     PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
03304 }
03305 
03306 Datum
03307 array_gt(PG_FUNCTION_ARGS)
03308 {
03309     PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
03310 }
03311 
03312 Datum
03313 array_le(PG_FUNCTION_ARGS)
03314 {
03315     PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
03316 }
03317 
03318 Datum
03319 array_ge(PG_FUNCTION_ARGS)
03320 {
03321     PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
03322 }
03323 
03324 Datum
03325 btarraycmp(PG_FUNCTION_ARGS)
03326 {
03327     PG_RETURN_INT32(array_cmp(fcinfo));
03328 }
03329 
03330 /*
03331  * array_cmp()
03332  * Internal comparison function for arrays.
03333  *
03334  * Returns -1, 0 or 1
03335  */
03336 static int
03337 array_cmp(FunctionCallInfo fcinfo)
03338 {
03339     ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
03340     ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
03341     Oid         collation = PG_GET_COLLATION();
03342     int         ndims1 = ARR_NDIM(array1);
03343     int         ndims2 = ARR_NDIM(array2);
03344     int        *dims1 = ARR_DIMS(array1);
03345     int        *dims2 = ARR_DIMS(array2);
03346     int         nitems1 = ArrayGetNItems(ndims1, dims1);
03347     int         nitems2 = ArrayGetNItems(ndims2, dims2);
03348     Oid         element_type = ARR_ELEMTYPE(array1);
03349     int         result = 0;
03350     TypeCacheEntry *typentry;
03351     int         typlen;
03352     bool        typbyval;
03353     char        typalign;
03354     int         min_nitems;
03355     char       *ptr1;
03356     char       *ptr2;
03357     bits8      *bitmap1;
03358     bits8      *bitmap2;
03359     int         bitmask;
03360     int         i;
03361     FunctionCallInfoData locfcinfo;
03362 
03363     if (element_type != ARR_ELEMTYPE(array2))
03364         ereport(ERROR,
03365                 (errcode(ERRCODE_DATATYPE_MISMATCH),
03366                  errmsg("cannot compare arrays of different element types")));
03367 
03368     /*
03369      * We arrange to look up the comparison function only once per series of
03370      * calls, assuming the element type doesn't change underneath us. The
03371      * typcache is used so that we have no memory leakage when being used as
03372      * an index support function.
03373      */
03374     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
03375     if (typentry == NULL ||
03376         typentry->type_id != element_type)
03377     {
03378         typentry = lookup_type_cache(element_type,
03379                                      TYPECACHE_CMP_PROC_FINFO);
03380         if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
03381             ereport(ERROR,
03382                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
03383                errmsg("could not identify a comparison function for type %s",
03384                       format_type_be(element_type))));
03385         fcinfo->flinfo->fn_extra = (void *) typentry;
03386     }
03387     typlen = typentry->typlen;
03388     typbyval = typentry->typbyval;
03389     typalign = typentry->typalign;
03390 
03391     /*
03392      * apply the operator to each pair of array elements.
03393      */
03394     InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
03395                              collation, NULL, NULL);
03396 
03397     /* Loop over source data */
03398     min_nitems = Min(nitems1, nitems2);
03399     ptr1 = ARR_DATA_PTR(array1);
03400     ptr2 = ARR_DATA_PTR(array2);
03401     bitmap1 = ARR_NULLBITMAP(array1);
03402     bitmap2 = ARR_NULLBITMAP(array2);
03403     bitmask = 1;                /* use same bitmask for both arrays */
03404 
03405     for (i = 0; i < min_nitems; i++)
03406     {
03407         Datum       elt1;
03408         Datum       elt2;
03409         bool        isnull1;
03410         bool        isnull2;
03411         int32       cmpresult;
03412 
03413         /* Get elements, checking for NULL */
03414         if (bitmap1 && (*bitmap1 & bitmask) == 0)
03415         {
03416             isnull1 = true;
03417             elt1 = (Datum) 0;
03418         }
03419         else
03420         {
03421             isnull1 = false;
03422             elt1 = fetch_att(ptr1, typbyval, typlen);
03423             ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
03424             ptr1 = (char *) att_align_nominal(ptr1, typalign);
03425         }
03426 
03427         if (bitmap2 && (*bitmap2 & bitmask) == 0)
03428         {
03429             isnull2 = true;
03430             elt2 = (Datum) 0;
03431         }
03432         else
03433         {
03434             isnull2 = false;
03435             elt2 = fetch_att(ptr2, typbyval, typlen);
03436             ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
03437             ptr2 = (char *) att_align_nominal(ptr2, typalign);
03438         }
03439 
03440         /* advance bitmap pointers if any */
03441         bitmask <<= 1;
03442         if (bitmask == 0x100)
03443         {
03444             if (bitmap1)
03445                 bitmap1++;
03446             if (bitmap2)
03447                 bitmap2++;
03448             bitmask = 1;
03449         }
03450 
03451         /*
03452          * We consider two NULLs equal; NULL > not-NULL.
03453          */
03454         if (isnull1 && isnull2)
03455             continue;
03456         if (isnull1)
03457         {
03458             /* arg1 is greater than arg2 */
03459             result = 1;
03460             break;
03461         }
03462         if (isnull2)
03463         {
03464             /* arg1 is less than arg2 */
03465             result = -1;
03466             break;
03467         }
03468 
03469         /* Compare the pair of elements */
03470         locfcinfo.arg[0] = elt1;
03471         locfcinfo.arg[1] = elt2;
03472         locfcinfo.argnull[0] = false;
03473         locfcinfo.argnull[1] = false;
03474         locfcinfo.isnull = false;
03475         cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
03476 
03477         if (cmpresult == 0)
03478             continue;           /* equal */
03479 
03480         if (cmpresult < 0)
03481         {
03482             /* arg1 is less than arg2 */
03483             result = -1;
03484             break;
03485         }
03486         else
03487         {
03488             /* arg1 is greater than arg2 */
03489             result = 1;
03490             break;
03491         }
03492     }
03493 
03494     /*
03495      * If arrays contain same data (up to end of shorter one), apply
03496      * additional rules to sort by dimensionality.  The relative significance
03497      * of the different bits of information is historical; mainly we just care
03498      * that we don't say "equal" for arrays of different dimensionality.
03499      */
03500     if (result == 0)
03501     {
03502         if (nitems1 != nitems2)
03503             result = (nitems1 < nitems2) ? -1 : 1;
03504         else if (ndims1 != ndims2)
03505             result = (ndims1 < ndims2) ? -1 : 1;
03506         else
03507         {
03508             /* this relies on LB array immediately following DIMS array */
03509             for (i = 0; i < ndims1 * 2; i++)
03510             {
03511                 if (dims1[i] != dims2[i])
03512                 {
03513                     result = (dims1[i] < dims2[i]) ? -1 : 1;
03514                     break;
03515                 }
03516             }
03517         }
03518     }
03519 
03520     /* Avoid leaking memory when handed toasted input. */
03521     PG_FREE_IF_COPY(array1, 0);
03522     PG_FREE_IF_COPY(array2, 1);
03523 
03524     return result;
03525 }
03526 
03527 
03528 /*-----------------------------------------------------------------------------
03529  * array hashing
03530  *      Hash the elements and combine the results.
03531  *----------------------------------------------------------------------------
03532  */
03533 
03534 Datum
03535 hash_array(PG_FUNCTION_ARGS)
03536 {
03537     ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
03538     int         ndims = ARR_NDIM(array);
03539     int        *dims = ARR_DIMS(array);
03540     Oid         element_type = ARR_ELEMTYPE(array);
03541     uint32      result = 1;
03542     int         nitems;
03543     TypeCacheEntry *typentry;
03544     int         typlen;
03545     bool        typbyval;
03546     char        typalign;
03547     char       *ptr;
03548     bits8      *bitmap;
03549     int         bitmask;
03550     int         i;
03551     FunctionCallInfoData locfcinfo;
03552 
03553     /*
03554      * We arrange to look up the hash function only once per series of calls,
03555      * assuming the element type doesn't change underneath us.  The typcache
03556      * is used so that we have no memory leakage when being used as an index
03557      * support function.
03558      */
03559     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
03560     if (typentry == NULL ||
03561         typentry->type_id != element_type)
03562     {
03563         typentry = lookup_type_cache(element_type,
03564                                      TYPECACHE_HASH_PROC_FINFO);
03565         if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
03566             ereport(ERROR,
03567                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
03568                      errmsg("could not identify a hash function for type %s",
03569                             format_type_be(element_type))));
03570         fcinfo->flinfo->fn_extra = (void *) typentry;
03571     }
03572     typlen = typentry->typlen;
03573     typbyval = typentry->typbyval;
03574     typalign = typentry->typalign;
03575 
03576     /*
03577      * apply the hash function to each array element.
03578      */
03579     InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
03580                              InvalidOid, NULL, NULL);
03581 
03582     /* Loop over source data */
03583     nitems = ArrayGetNItems(ndims, dims);
03584     ptr = ARR_DATA_PTR(array);
03585     bitmap = ARR_NULLBITMAP(array);
03586     bitmask = 1;
03587 
03588     for (i = 0; i < nitems; i++)
03589     {
03590         uint32      elthash;
03591 
03592         /* Get element, checking for NULL */
03593         if (bitmap && (*bitmap & bitmask) == 0)
03594         {
03595             /* Treat nulls as having hashvalue 0 */
03596             elthash = 0;
03597         }
03598         else
03599         {
03600             Datum       elt;
03601 
03602             elt = fetch_att(ptr, typbyval, typlen);
03603             ptr = att_addlength_pointer(ptr, typlen, ptr);
03604             ptr = (char *) att_align_nominal(ptr, typalign);
03605 
03606             /* Apply the hash function */
03607             locfcinfo.arg[0] = elt;
03608             locfcinfo.argnull[0] = false;
03609             locfcinfo.isnull = false;
03610             elthash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
03611         }
03612 
03613         /* advance bitmap pointer if any */
03614         if (bitmap)
03615         {
03616             bitmask <<= 1;
03617             if (bitmask == 0x100)
03618             {
03619                 bitmap++;
03620                 bitmask = 1;
03621             }
03622         }
03623 
03624         /*
03625          * Combine hash values of successive elements by multiplying the
03626          * current value by 31 and adding on the new element's hash value.
03627          *
03628          * The result is a sum in which each element's hash value is
03629          * multiplied by a different power of 31. This is modulo 2^32
03630          * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
03631          * order 2^27. So for arrays of up to 2^27 elements, each element's
03632          * hash value is multiplied by a different (odd) number, resulting in
03633          * a good mixing of all the elements' hash values.
03634          */
03635         result = (result << 5) - result + elthash;
03636     }
03637 
03638     /* Avoid leaking memory when handed toasted input. */
03639     PG_FREE_IF_COPY(array, 0);
03640 
03641     PG_RETURN_UINT32(result);
03642 }
03643 
03644 
03645 /*-----------------------------------------------------------------------------
03646  * array overlap/containment comparisons
03647  *      These use the same methods of comparing array elements as array_eq.
03648  *      We consider only the elements of the arrays, ignoring dimensionality.
03649  *----------------------------------------------------------------------------
03650  */
03651 
03652 /*
03653  * array_contain_compare :
03654  *        compares two arrays for overlap/containment
03655  *
03656  * When matchall is true, return true if all members of array1 are in array2.
03657  * When matchall is false, return true if any members of array1 are in array2.
03658  */
03659 static bool
03660 array_contain_compare(ArrayType *array1, ArrayType *array2, Oid collation,
03661                       bool matchall, void **fn_extra)
03662 {
03663     bool        result = matchall;
03664     Oid         element_type = ARR_ELEMTYPE(array1);
03665     TypeCacheEntry *typentry;
03666     int         nelems1;
03667     Datum      *values2;
03668     bool       *nulls2;
03669     int         nelems2;
03670     int         typlen;
03671     bool        typbyval;
03672     char        typalign;
03673     char       *ptr1;
03674     bits8      *bitmap1;
03675     int         bitmask;
03676     int         i;
03677     int         j;
03678     FunctionCallInfoData locfcinfo;
03679 
03680     if (element_type != ARR_ELEMTYPE(array2))
03681         ereport(ERROR,
03682                 (errcode(ERRCODE_DATATYPE_MISMATCH),
03683                  errmsg("cannot compare arrays of different element types")));
03684 
03685     /*
03686      * We arrange to look up the equality function only once per series of
03687      * calls, assuming the element type doesn't change underneath us.  The
03688      * typcache is used so that we have no memory leakage when being used as
03689      * an index support function.
03690      */
03691     typentry = (TypeCacheEntry *) *fn_extra;
03692     if (typentry == NULL ||
03693         typentry->type_id != element_type)
03694     {
03695         typentry = lookup_type_cache(element_type,
03696                                      TYPECACHE_EQ_OPR_FINFO);
03697         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
03698             ereport(ERROR,
03699                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
03700                 errmsg("could not identify an equality operator for type %s",
03701                        format_type_be(element_type))));
03702         *fn_extra = (void *) typentry;
03703     }
03704     typlen = typentry->typlen;
03705     typbyval = typentry->typbyval;
03706     typalign = typentry->typalign;
03707 
03708     /*
03709      * Since we probably will need to scan array2 multiple times, it's
03710      * worthwhile to use deconstruct_array on it.  We scan array1 the hard way
03711      * however, since we very likely won't need to look at all of it.
03712      */
03713     deconstruct_array(array2, element_type, typlen, typbyval, typalign,
03714                       &values2, &nulls2, &nelems2);
03715 
03716     /*
03717      * Apply the comparison operator to each pair of array elements.
03718      */
03719     InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
03720                              collation, NULL, NULL);
03721 
03722     /* Loop over source data */
03723     nelems1 = ArrayGetNItems(ARR_NDIM(array1), ARR_DIMS(array1));
03724     ptr1 = ARR_DATA_PTR(array1);
03725     bitmap1 = ARR_NULLBITMAP(array1);
03726     bitmask = 1;
03727 
03728     for (i = 0; i < nelems1; i++)
03729     {
03730         Datum       elt1;
03731         bool        isnull1;
03732 
03733         /* Get element, checking for NULL */
03734         if (bitmap1 && (*bitmap1 & bitmask) == 0)
03735         {
03736             isnull1 = true;
03737             elt1 = (Datum) 0;
03738         }
03739         else
03740         {
03741             isnull1 = false;
03742             elt1 = fetch_att(ptr1, typbyval, typlen);
03743             ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
03744             ptr1 = (char *) att_align_nominal(ptr1, typalign);
03745         }
03746 
03747         /* advance bitmap pointer if any */
03748         bitmask <<= 1;
03749         if (bitmask == 0x100)
03750         {
03751             if (bitmap1)
03752                 bitmap1++;
03753             bitmask = 1;
03754         }
03755 
03756         /*
03757          * We assume that the comparison operator is strict, so a NULL can't
03758          * match anything.  XXX this diverges from the "NULL=NULL" behavior of
03759          * array_eq, should we act like that?
03760          */
03761         if (isnull1)
03762         {
03763             if (matchall)
03764             {
03765                 result = false;
03766                 break;
03767             }
03768             continue;
03769         }
03770 
03771         for (j = 0; j < nelems2; j++)
03772         {
03773             Datum       elt2 = values2[j];
03774             bool        isnull2 = nulls2[j];
03775             bool        oprresult;
03776 
03777             if (isnull2)
03778                 continue;       /* can't match */
03779 
03780             /*
03781              * Apply the operator to the element pair
03782              */
03783             locfcinfo.arg[0] = elt1;
03784             locfcinfo.arg[1] = elt2;
03785             locfcinfo.argnull[0] = false;
03786             locfcinfo.argnull[1] = false;
03787             locfcinfo.isnull = false;
03788             oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
03789             if (oprresult)
03790                 break;
03791         }
03792 
03793         if (j < nelems2)
03794         {
03795             /* found a match for elt1 */
03796             if (!matchall)
03797             {
03798                 result = true;
03799                 break;
03800             }
03801         }
03802         else
03803         {
03804             /* no match for elt1 */
03805             if (matchall)
03806             {
03807                 result = false;
03808                 break;
03809             }
03810         }
03811     }
03812 
03813     pfree(values2);
03814     pfree(nulls2);
03815 
03816     return result;
03817 }
03818 
03819 Datum
03820 arrayoverlap(PG_FUNCTION_ARGS)
03821 {
03822     ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
03823     ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
03824     Oid         collation = PG_GET_COLLATION();
03825     bool        result;
03826 
03827     result = array_contain_compare(array1, array2, collation, false,
03828                                    &fcinfo->flinfo->fn_extra);
03829 
03830     /* Avoid leaking memory when handed toasted input. */
03831     PG_FREE_IF_COPY(array1, 0);
03832     PG_FREE_IF_COPY(array2, 1);
03833 
03834     PG_RETURN_BOOL(result);
03835 }
03836 
03837 Datum
03838 arraycontains(PG_FUNCTION_ARGS)
03839 {
03840     ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
03841     ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
03842     Oid         collation = PG_GET_COLLATION();
03843     bool        result;
03844 
03845     result = array_contain_compare(array2, array1, collation, true,
03846                                    &fcinfo->flinfo->fn_extra);
03847 
03848     /* Avoid leaking memory when handed toasted input. */
03849     PG_FREE_IF_COPY(array1, 0);
03850     PG_FREE_IF_COPY(array2, 1);
03851 
03852     PG_RETURN_BOOL(result);
03853 }
03854 
03855 Datum
03856 arraycontained(PG_FUNCTION_ARGS)
03857 {
03858     ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
03859     ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
03860     Oid         collation = PG_GET_COLLATION();
03861     bool        result;
03862 
03863     result = array_contain_compare(array1, array2, collation, true,
03864                                    &fcinfo->flinfo->fn_extra);
03865 
03866     /* Avoid leaking memory when handed toasted input. */
03867     PG_FREE_IF_COPY(array1, 0);
03868     PG_FREE_IF_COPY(array2, 1);
03869 
03870     PG_RETURN_BOOL(result);
03871 }
03872 
03873 
03874 /*-----------------------------------------------------------------------------
03875  * Array iteration functions
03876  *      These functions are used to iterate efficiently through arrays
03877  *-----------------------------------------------------------------------------
03878  */
03879 
03880 /*
03881  * array_create_iterator --- set up to iterate through an array
03882  *
03883  * If slice_ndim is zero, we will iterate element-by-element; the returned
03884  * datums are of the array's element type.
03885  *
03886  * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
03887  * returned datums are of the same array type as 'arr', but of size
03888  * equal to the rightmost N dimensions of 'arr'.
03889  *
03890  * The passed-in array must remain valid for the lifetime of the iterator.
03891  */
03892 ArrayIterator
03893 array_create_iterator(ArrayType *arr, int slice_ndim)
03894 {
03895     ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
03896 
03897     /*
03898      * Sanity-check inputs --- caller should have got this right already
03899      */
03900     Assert(PointerIsValid(arr));
03901     if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
03902         elog(ERROR, "invalid arguments to array_create_iterator");
03903 
03904     /*
03905      * Remember basic info about the array and its element type
03906      */
03907     iterator->arr = arr;
03908     iterator->nullbitmap = ARR_NULLBITMAP(arr);
03909     iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
03910     get_typlenbyvalalign(ARR_ELEMTYPE(arr),
03911                          &iterator->typlen,
03912                          &iterator->typbyval,
03913                          &iterator->typalign);
03914 
03915     /*
03916      * Remember the slicing parameters.
03917      */
03918     iterator->slice_ndim = slice_ndim;
03919 
03920     if (slice_ndim > 0)
03921     {
03922         /*
03923          * Get pointers into the array's dims and lbound arrays to represent
03924          * the dims/lbound arrays of a slice.  These are the same as the
03925          * rightmost N dimensions of the array.
03926          */
03927         iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
03928         iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
03929 
03930         /*
03931          * Compute number of elements in a slice.
03932          */
03933         iterator->slice_len = ArrayGetNItems(slice_ndim,
03934                                              iterator->slice_dims);
03935 
03936         /*
03937          * Create workspace for building sub-arrays.
03938          */
03939         iterator->slice_values = (Datum *)
03940             palloc(iterator->slice_len * sizeof(Datum));
03941         iterator->slice_nulls = (bool *)
03942             palloc(iterator->slice_len * sizeof(bool));
03943     }
03944 
03945     /*
03946      * Initialize our data pointer and linear element number.  These will
03947      * advance through the array during array_iterate().
03948      */
03949     iterator->data_ptr = ARR_DATA_PTR(arr);
03950     iterator->current_item = 0;
03951 
03952     return iterator;
03953 }
03954 
03955 /*
03956  * Iterate through the array referenced by 'iterator'.
03957  *
03958  * As long as there is another element (or slice), return it into
03959  * *value / *isnull, and return true.  Return false when no more data.
03960  */
03961 bool
03962 array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
03963 {
03964     /* Done if we have reached the end of the array */
03965     if (iterator->current_item >= iterator->nitems)
03966         return false;
03967 
03968     if (iterator->slice_ndim == 0)
03969     {
03970         /*
03971          * Scalar case: return one element.
03972          */
03973         if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
03974         {
03975             *isnull = true;
03976             *value = (Datum) 0;
03977         }
03978         else
03979         {
03980             /* non-NULL, so fetch the individual Datum to return */
03981             char       *p = iterator->data_ptr;
03982 
03983             *isnull = false;
03984             *value = fetch_att(p, iterator->typbyval, iterator->typlen);
03985 
03986             /* Move our data pointer forward to the next element */
03987             p = att_addlength_pointer(p, iterator->typlen, p);
03988             p = (char *) att_align_nominal(p, iterator->typalign);
03989             iterator->data_ptr = p;
03990         }
03991     }
03992     else
03993     {
03994         /*
03995          * Slice case: build and return an array of the requested size.
03996          */
03997         ArrayType  *result;
03998         Datum      *values = iterator->slice_values;
03999         bool       *nulls = iterator->slice_nulls;
04000         char       *p = iterator->data_ptr;
04001         int         i;
04002 
04003         for (i = 0; i < iterator->slice_len; i++)
04004         {
04005             if (array_get_isnull(iterator->nullbitmap,
04006                                  iterator->current_item++))
04007             {
04008                 nulls[i] = true;
04009                 values[i] = (Datum) 0;
04010             }
04011             else
04012             {
04013                 nulls[i] = false;
04014                 values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
04015 
04016                 /* Move our data pointer forward to the next element */
04017                 p = att_addlength_pointer(p, iterator->typlen, p);
04018                 p = (char *) att_align_nominal(p, iterator->typalign);
04019             }
04020         }
04021 
04022         iterator->data_ptr = p;
04023 
04024         result = construct_md_array(values,
04025                                     nulls,
04026                                     iterator->slice_ndim,
04027                                     iterator->slice_dims,
04028                                     iterator->slice_lbound,
04029                                     ARR_ELEMTYPE(iterator->arr),
04030                                     iterator->typlen,
04031                                     iterator->typbyval,
04032                                     iterator->typalign);
04033 
04034         *isnull = false;
04035         *value = PointerGetDatum(result);
04036     }
04037 
04038     return true;
04039 }
04040 
04041 /*
04042  * Release an ArrayIterator data structure
04043  */
04044 void
04045 array_free_iterator(ArrayIterator iterator)
04046 {
04047     if (iterator->slice_ndim > 0)
04048     {
04049         pfree(iterator->slice_values);
04050         pfree(iterator->slice_nulls);
04051     }
04052     pfree(iterator);
04053 }
04054 
04055 
04056 /***************************************************************************/
04057 /******************|          Support  Routines           |*****************/
04058 /***************************************************************************/
04059 
04060 /*
04061  * Check whether a specific array element is NULL
04062  *
04063  * nullbitmap: pointer to array's null bitmap (NULL if none)
04064  * offset: 0-based linear element number of array element
04065  */
04066 static bool
04067 array_get_isnull(const bits8 *nullbitmap, int offset)
04068 {
04069     if (nullbitmap == NULL)
04070         return false;           /* assume not null */
04071     if (nullbitmap[offset / 8] & (1 << (offset % 8)))
04072         return false;           /* not null */
04073     return true;
04074 }
04075 
04076 /*
04077  * Set a specific array element's null-bitmap entry
04078  *
04079  * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
04080  * offset: 0-based linear element number of array element
04081  * isNull: null status to set
04082  */
04083 static void
04084 array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
04085 {
04086     int         bitmask;
04087 
04088     nullbitmap += offset / 8;
04089     bitmask = 1 << (offset % 8);
04090     if (isNull)
04091         *nullbitmap &= ~bitmask;
04092     else
04093         *nullbitmap |= bitmask;
04094 }
04095 
04096 /*
04097  * Fetch array element at pointer, converted correctly to a Datum
04098  *
04099  * Caller must have handled case of NULL element
04100  */
04101 static Datum
04102 ArrayCast(char *value, bool byval, int len)
04103 {
04104     return fetch_att(value, byval, len);
04105 }
04106 
04107 /*
04108  * Copy datum to *dest and return total space used (including align padding)
04109  *
04110  * Caller must have handled case of NULL element
04111  */
04112 static int
04113 ArrayCastAndSet(Datum src,
04114                 int typlen,
04115                 bool typbyval,
04116                 char typalign,
04117                 char *dest)
04118 {
04119     int         inc;
04120 
04121     if (typlen > 0)
04122     {
04123         if (typbyval)
04124             store_att_byval(dest, src, typlen);
04125         else
04126             memmove(dest, DatumGetPointer(src), typlen);
04127         inc = att_align_nominal(typlen, typalign);
04128     }
04129     else
04130     {
04131         Assert(!typbyval);
04132         inc = att_addlength_datum(0, typlen, src);
04133         memmove(dest, DatumGetPointer(src), inc);
04134         inc = att_align_nominal(inc, typalign);
04135     }
04136 
04137     return inc;
04138 }
04139 
04140 /*
04141  * Advance ptr over nitems array elements
04142  *
04143  * ptr: starting location in array
04144  * offset: 0-based linear element number of first element (the one at *ptr)
04145  * nullbitmap: start of array's null bitmap, or NULL if none
04146  * nitems: number of array elements to advance over (>= 0)
04147  * typlen, typbyval, typalign: storage parameters of array element datatype
04148  *
04149  * It is caller's responsibility to ensure that nitems is within range
04150  */
04151 static char *
04152 array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
04153            int typlen, bool typbyval, char typalign)
04154 {
04155     int         bitmask;
04156     int         i;
04157 
04158     /* easy if fixed-size elements and no NULLs */
04159     if (typlen > 0 && !nullbitmap)
04160         return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
04161 
04162     /* seems worth having separate loops for NULL and no-NULLs cases */
04163     if (nullbitmap)
04164     {
04165         nullbitmap += offset / 8;
04166         bitmask = 1 << (offset % 8);
04167 
04168         for (i = 0; i < nitems; i++)
04169         {
04170             if (*nullbitmap & bitmask)
04171             {
04172                 ptr = att_addlength_pointer(ptr, typlen, ptr);
04173                 ptr = (char *) att_align_nominal(ptr, typalign);
04174             }
04175             bitmask <<= 1;
04176             if (bitmask == 0x100)
04177             {
04178                 nullbitmap++;
04179                 bitmask = 1;
04180             }
04181         }
04182     }
04183     else
04184     {
04185         for (i = 0; i < nitems; i++)
04186         {
04187             ptr = att_addlength_pointer(ptr, typlen, ptr);
04188             ptr = (char *) att_align_nominal(ptr, typalign);
04189         }
04190     }
04191     return ptr;
04192 }
04193 
04194 /*
04195  * Compute total size of the nitems array elements starting at *ptr
04196  *
04197  * Parameters same as for array_seek
04198  */
04199 static int
04200 array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
04201                   int typlen, bool typbyval, char typalign)
04202 {
04203     return array_seek(ptr, offset, nullbitmap, nitems,
04204                       typlen, typbyval, typalign) - ptr;
04205 }
04206 
04207 /*
04208  * Copy nitems array elements from srcptr to destptr
04209  *
04210  * destptr: starting destination location (must be enough room!)
04211  * nitems: number of array elements to copy (>= 0)
04212  * srcptr: starting location in source array
04213  * offset: 0-based linear element number of first element (the one at *srcptr)
04214  * nullbitmap: start of source array's null bitmap, or NULL if none
04215  * typlen, typbyval, typalign: storage parameters of array element datatype
04216  *
04217  * Returns number of bytes copied
04218  *
04219  * NB: this does not take care of setting up the destination's null bitmap!
04220  */
04221 static int
04222 array_copy(char *destptr, int nitems,
04223            char *srcptr, int offset, bits8 *nullbitmap,
04224            int typlen, bool typbyval, char typalign)
04225 {
04226     int         numbytes;
04227 
04228     numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
04229                                  typlen, typbyval, typalign);
04230     memcpy(destptr, srcptr, numbytes);
04231     return numbytes;
04232 }
04233 
04234 /*
04235  * Copy nitems null-bitmap bits from source to destination
04236  *
04237  * destbitmap: start of destination array's null bitmap (mustn't be NULL)
04238  * destoffset: 0-based linear element number of first dest element
04239  * srcbitmap: start of source array's null bitmap, or NULL if none
04240  * srcoffset: 0-based linear element number of first source element
04241  * nitems: number of bits to copy (>= 0)
04242  *
04243  * If srcbitmap is NULL then we assume the source is all-non-NULL and
04244  * fill 1's into the destination bitmap.  Note that only the specified
04245  * bits in the destination map are changed, not any before or after.
04246  *
04247  * Note: this could certainly be optimized using standard bitblt methods.
04248  * However, it's not clear that the typical Postgres array has enough elements
04249  * to make it worth worrying too much.  For the moment, KISS.
04250  */
04251 void
04252 array_bitmap_copy(bits8 *destbitmap, int destoffset,
04253                   const bits8 *srcbitmap, int srcoffset,
04254                   int nitems)
04255 {
04256     int         destbitmask,
04257                 destbitval,
04258                 srcbitmask,
04259                 srcbitval;
04260 
04261     Assert(destbitmap);
04262     if (nitems <= 0)
04263         return;                 /* don't risk fetch off end of memory */
04264     destbitmap += destoffset / 8;
04265     destbitmask = 1 << (destoffset % 8);
04266     destbitval = *destbitmap;
04267     if (srcbitmap)
04268     {
04269         srcbitmap += srcoffset / 8;
04270         srcbitmask = 1 << (srcoffset % 8);
04271         srcbitval = *srcbitmap;
04272         while (nitems-- > 0)
04273         {
04274             if (srcbitval & srcbitmask)
04275                 destbitval |= destbitmask;
04276             else
04277                 destbitval &= ~destbitmask;
04278             destbitmask <<= 1;
04279             if (destbitmask == 0x100)
04280             {
04281                 *destbitmap++ = destbitval;
04282                 destbitmask = 1;
04283                 if (nitems > 0)
04284                     destbitval = *destbitmap;
04285             }
04286             srcbitmask <<= 1;
04287             if (srcbitmask == 0x100)
04288             {
04289                 srcbitmap++;
04290                 srcbitmask = 1;
04291                 if (nitems > 0)
04292                     srcbitval = *srcbitmap;
04293             }
04294         }
04295         if (destbitmask != 1)
04296             *destbitmap = destbitval;
04297     }
04298     else
04299     {
04300         while (nitems-- > 0)
04301         {
04302             destbitval |= destbitmask;
04303             destbitmask <<= 1;
04304             if (destbitmask == 0x100)
04305             {
04306                 *destbitmap++ = destbitval;
04307                 destbitmask = 1;
04308                 if (nitems > 0)
04309                     destbitval = *destbitmap;
04310             }
04311         }
04312         if (destbitmask != 1)
04313             *destbitmap = destbitval;
04314     }
04315 }
04316 
04317 /*
04318  * Compute space needed for a slice of an array
04319  *
04320  * We assume the caller has verified that the slice coordinates are valid.
04321  */
04322 static int
04323 array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
04324                  int ndim, int *dim, int *lb,
04325                  int *st, int *endp,
04326                  int typlen, bool typbyval, char typalign)
04327 {
04328     int         src_offset,
04329                 span[MAXDIM],
04330                 prod[MAXDIM],
04331                 dist[MAXDIM],
04332                 indx[MAXDIM];
04333     char       *ptr;
04334     int         i,
04335                 j,
04336                 inc;
04337     int         count = 0;
04338 
04339     mda_get_range(ndim, span, st, endp);
04340 
04341     /* Pretty easy for fixed element length without nulls ... */
04342     if (typlen > 0 && !arraynullsptr)
04343         return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
04344 
04345     /* Else gotta do it the hard way */
04346     src_offset = ArrayGetOffset(ndim, dim, lb, st);
04347     ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
04348                      typlen, typbyval, typalign);
04349     mda_get_prod(ndim, dim, prod);
04350     mda_get_offset_values(ndim, dist, prod, span);
04351     for (i = 0; i < ndim; i++)
04352         indx[i] = 0;
04353     j = ndim - 1;
04354     do
04355     {
04356         if (dist[j])
04357         {
04358             ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
04359                              typlen, typbyval, typalign);
04360             src_offset += dist[j];
04361         }
04362         if (!array_get_isnull(arraynullsptr, src_offset))
04363         {
04364             inc = att_addlength_pointer(0, typlen, ptr);
04365             inc = att_align_nominal(inc, typalign);
04366             ptr += inc;
04367             count += inc;
04368         }
04369         src_offset++;
04370     } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
04371     return count;
04372 }
04373 
04374 /*
04375  * Extract a slice of an array into consecutive elements in the destination
04376  * array.
04377  *
04378  * We assume the caller has verified that the slice coordinates are valid,
04379  * allocated enough storage for the result, and initialized the header
04380  * of the new array.
04381  */
04382 static void
04383 array_extract_slice(ArrayType *newarray,
04384                     int ndim,
04385                     int *dim,
04386                     int *lb,
04387                     char *arraydataptr,
04388                     bits8 *arraynullsptr,
04389                     int *st,
04390                     int *endp,
04391                     int typlen,
04392                     bool typbyval,
04393                     char typalign)
04394 {
04395     char       *destdataptr = ARR_DATA_PTR(newarray);
04396     bits8      *destnullsptr = ARR_NULLBITMAP(newarray);
04397     char       *srcdataptr;
04398     int         src_offset,
04399                 dest_offset,
04400                 prod[MAXDIM],
04401                 span[MAXDIM],
04402                 dist[MAXDIM],
04403                 indx[MAXDIM];
04404     int         i,
04405                 j,
04406                 inc;
04407 
04408     src_offset = ArrayGetOffset(ndim, dim, lb, st);
04409     srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
04410                             typlen, typbyval, typalign);
04411     mda_get_prod(ndim, dim, prod);
04412     mda_get_range(ndim, span, st, endp);
04413     mda_get_offset_values(ndim, dist, prod, span);
04414     for (i = 0; i < ndim; i++)
04415         indx[i] = 0;
04416     dest_offset = 0;
04417     j = ndim - 1;
04418     do
04419     {
04420         if (dist[j])
04421         {
04422             /* skip unwanted elements */
04423             srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
04424                                     dist[j],
04425                                     typlen, typbyval, typalign);
04426             src_offset += dist[j];
04427         }
04428         inc = array_copy(destdataptr, 1,
04429                          srcdataptr, src_offset, arraynullsptr,
04430                          typlen, typbyval, typalign);
04431         if (destnullsptr)
04432             array_bitmap_copy(destnullsptr, dest_offset,
04433                               arraynullsptr, src_offset,
04434                               1);
04435         destdataptr += inc;
04436         srcdataptr += inc;
04437         src_offset++;
04438         dest_offset++;
04439     } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
04440 }
04441 
04442 /*
04443  * Insert a slice into an array.
04444  *
04445  * ndim/dim[]/lb[] are dimensions of the original array.  A new array with
04446  * those same dimensions is to be constructed.  destArray must already
04447  * have been allocated and its header initialized.
04448  *
04449  * st[]/endp[] identify the slice to be replaced.  Elements within the slice
04450  * volume are taken from consecutive elements of the srcArray; elements
04451  * outside it are copied from origArray.
04452  *
04453  * We assume the caller has verified that the slice coordinates are valid.
04454  */
04455 static void
04456 array_insert_slice(ArrayType *destArray,
04457                    ArrayType *origArray,
04458                    ArrayType *srcArray,
04459                    int ndim,
04460                    int *dim,
04461                    int *lb,
04462                    int *st,
04463                    int *endp,
04464                    int typlen,
04465                    bool typbyval,
04466                    char typalign)
04467 {
04468     char       *destPtr = ARR_DATA_PTR(destArray);
04469     char       *origPtr = ARR_DATA_PTR(origArray);
04470     char       *srcPtr = ARR_DATA_PTR(srcArray);
04471     bits8      *destBitmap = ARR_NULLBITMAP(destArray);
04472     bits8      *origBitmap = ARR_NULLBITMAP(origArray);
04473     bits8      *srcBitmap = ARR_NULLBITMAP(srcArray);
04474     int         orignitems = ArrayGetNItems(ARR_NDIM(origArray),
04475                                             ARR_DIMS(origArray));
04476     int         dest_offset,
04477                 orig_offset,
04478                 src_offset,
04479                 prod[MAXDIM],
04480                 span[MAXDIM],
04481                 dist[MAXDIM],
04482                 indx[MAXDIM];
04483     int         i,
04484                 j,
04485                 inc;
04486 
04487     dest_offset = ArrayGetOffset(ndim, dim, lb, st);
04488     /* copy items before the slice start */
04489     inc = array_copy(destPtr, dest_offset,
04490                      origPtr, 0, origBitmap,
04491                      typlen, typbyval, typalign);
04492     destPtr += inc;
04493     origPtr += inc;
04494     if (destBitmap)
04495         array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
04496     orig_offset = dest_offset;
04497     mda_get_prod(ndim, dim, prod);
04498     mda_get_range(ndim, span, st, endp);
04499     mda_get_offset_values(ndim, dist, prod, span);
04500     for (i = 0; i < ndim; i++)
04501         indx[i] = 0;
04502     src_offset = 0;
04503     j = ndim - 1;
04504     do
04505     {
04506         /* Copy/advance over elements between here and next part of slice */
04507         if (dist[j])
04508         {
04509             inc = array_copy(destPtr, dist[j],
04510                              origPtr, orig_offset, origBitmap,
04511                              typlen, typbyval, typalign);
04512             destPtr += inc;
04513             origPtr += inc;
04514             if (destBitmap)
04515                 array_bitmap_copy(destBitmap, dest_offset,
04516                                   origBitmap, orig_offset,
04517                                   dist[j]);
04518             dest_offset += dist[j];
04519             orig_offset += dist[j];
04520         }
04521         /* Copy new element at this slice position */
04522         inc = array_copy(destPtr, 1,
04523                          srcPtr, src_offset, srcBitmap,
04524                          typlen, typbyval, typalign);
04525         if (destBitmap)
04526             array_bitmap_copy(destBitmap, dest_offset,
04527                               srcBitmap, src_offset,
04528                               1);
04529         destPtr += inc;
04530         srcPtr += inc;
04531         dest_offset++;
04532         src_offset++;
04533         /* Advance over old element at this slice position */
04534         origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
04535                              typlen, typbyval, typalign);
04536         orig_offset++;
04537     } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
04538 
04539     /* don't miss any data at the end */
04540     array_copy(destPtr, orignitems - orig_offset,
04541                origPtr, orig_offset, origBitmap,
04542                typlen, typbyval, typalign);
04543     if (destBitmap)
04544         array_bitmap_copy(destBitmap, dest_offset,
04545                           origBitmap, orig_offset,
04546                           orignitems - orig_offset);
04547 }
04548 
04549 /*
04550  * accumArrayResult - accumulate one (more) Datum for an array result
04551  *
04552  *  astate is working state (NULL on first call)
04553  *  rcontext is where to keep working state
04554  */
04555 ArrayBuildState *
04556 accumArrayResult(ArrayBuildState *astate,
04557                  Datum dvalue, bool disnull,
04558                  Oid element_type,
04559                  MemoryContext rcontext)
04560 {
04561     MemoryContext arr_context,
04562                 oldcontext;
04563 
04564     if (astate == NULL)
04565     {
04566         /* First time through --- initialize */
04567 
04568         /* Make a temporary context to hold all the junk */
04569         arr_context = AllocSetContextCreate(rcontext,
04570                                             "accumArrayResult",
04571                                             ALLOCSET_DEFAULT_MINSIZE,
04572                                             ALLOCSET_DEFAULT_INITSIZE,
04573                                             ALLOCSET_DEFAULT_MAXSIZE);
04574         oldcontext = MemoryContextSwitchTo(arr_context);
04575         astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
04576         astate->mcontext = arr_context;
04577         astate->alen = 64;      /* arbitrary starting array size */
04578         astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
04579         astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
04580         astate->nelems = 0;
04581         astate->element_type = element_type;
04582         get_typlenbyvalalign(element_type,
04583                              &astate->typlen,
04584                              &astate->typbyval,
04585                              &astate->typalign);
04586     }
04587     else
04588     {
04589         oldcontext = MemoryContextSwitchTo(astate->mcontext);
04590         Assert(astate->element_type == element_type);
04591         /* enlarge dvalues[]/dnulls[] if needed */
04592         if (astate->nelems >= astate->alen)
04593         {
04594             astate->alen *= 2;
04595             astate->dvalues = (Datum *)
04596                 repalloc(astate->dvalues, astate->alen * sizeof(Datum));
04597             astate->dnulls = (bool *)
04598                 repalloc(astate->dnulls, astate->alen * sizeof(bool));
04599         }
04600     }
04601 
04602     /*
04603      * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
04604      * it's varlena.  (You might think that detoasting is not needed here
04605      * because construct_md_array can detoast the array elements later.
04606      * However, we must not let construct_md_array modify the ArrayBuildState
04607      * because that would mean array_agg_finalfn damages its input, which is
04608      * verboten.  Also, this way frequently saves one copying step.)
04609      */
04610     if (!disnull && !astate->typbyval)
04611     {
04612         if (astate->typlen == -1)
04613             dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
04614         else
04615             dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
04616     }
04617 
04618     astate->dvalues[astate->nelems] = dvalue;
04619     astate->dnulls[astate->nelems] = disnull;
04620     astate->nelems++;
04621 
04622     MemoryContextSwitchTo(oldcontext);
04623 
04624     return astate;
04625 }
04626 
04627 /*
04628  * makeArrayResult - produce 1-D final result of accumArrayResult
04629  *
04630  *  astate is working state (not NULL)
04631  *  rcontext is where to construct result
04632  */
04633 Datum
04634 makeArrayResult(ArrayBuildState *astate,
04635                 MemoryContext rcontext)
04636 {
04637     int         dims[1];
04638     int         lbs[1];
04639 
04640     dims[0] = astate->nelems;
04641     lbs[0] = 1;
04642 
04643     return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
04644 }
04645 
04646 /*
04647  * makeMdArrayResult - produce multi-D final result of accumArrayResult
04648  *
04649  * beware: no check that specified dimensions match the number of values
04650  * accumulated.
04651  *
04652  *  astate is working state (not NULL)
04653  *  rcontext is where to construct result
04654  *  release is true if okay to release working state
04655  */
04656 Datum
04657 makeMdArrayResult(ArrayBuildState *astate,
04658                   int ndims,
04659                   int *dims,
04660                   int *lbs,
04661                   MemoryContext rcontext,
04662                   bool release)
04663 {
04664     ArrayType  *result;
04665     MemoryContext oldcontext;
04666 
04667     /* Build the final array result in rcontext */
04668     oldcontext = MemoryContextSwitchTo(rcontext);
04669 
04670     result = construct_md_array(astate->dvalues,
04671                                 astate->dnulls,
04672                                 ndims,
04673                                 dims,
04674                                 lbs,
04675                                 astate->element_type,
04676                                 astate->typlen,
04677                                 astate->typbyval,
04678                                 astate->typalign);
04679 
04680     MemoryContextSwitchTo(oldcontext);
04681 
04682     /* Clean up all the junk */
04683     if (release)
04684         MemoryContextDelete(astate->mcontext);
04685 
04686     return PointerGetDatum(result);
04687 }
04688 
04689 Datum
04690 array_larger(PG_FUNCTION_ARGS)
04691 {
04692     ArrayType  *v1,
04693                *v2,
04694                *result;
04695 
04696     v1 = PG_GETARG_ARRAYTYPE_P(0);
04697     v2 = PG_GETARG_ARRAYTYPE_P(1);
04698 
04699     result = ((array_cmp(fcinfo) > 0) ? v1 : v2);
04700 
04701     PG_RETURN_ARRAYTYPE_P(result);
04702 }
04703 
04704 Datum
04705 array_smaller(PG_FUNCTION_ARGS)
04706 {
04707     ArrayType  *v1,
04708                *v2,
04709                *result;
04710 
04711     v1 = PG_GETARG_ARRAYTYPE_P(0);
04712     v2 = PG_GETARG_ARRAYTYPE_P(1);
04713 
04714     result = ((array_cmp(fcinfo) < 0) ? v1 : v2);
04715 
04716     PG_RETURN_ARRAYTYPE_P(result);
04717 }
04718 
04719 
04720 typedef struct generate_subscripts_fctx
04721 {
04722     int32       lower;
04723     int32       upper;
04724     bool        reverse;
04725 } generate_subscripts_fctx;
04726 
04727 /*
04728  * generate_subscripts(array anyarray, dim int [, reverse bool])
04729  *      Returns all subscripts of the array for any dimension
04730  */
04731 Datum
04732 generate_subscripts(PG_FUNCTION_ARGS)
04733 {
04734     FuncCallContext *funcctx;
04735     MemoryContext oldcontext;
04736     generate_subscripts_fctx *fctx;
04737 
04738     /* stuff done only on the first call of the function */
04739     if (SRF_IS_FIRSTCALL())
04740     {
04741         ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
04742         int         reqdim = PG_GETARG_INT32(1);
04743         int        *lb,
04744                    *dimv;
04745 
04746         /* create a function context for cross-call persistence */
04747         funcctx = SRF_FIRSTCALL_INIT();
04748 
04749         /* Sanity check: does it look like an array at all? */
04750         if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
04751             SRF_RETURN_DONE(funcctx);
04752 
04753         /* Sanity check: was the requested dim valid */
04754         if (reqdim <= 0 || reqdim > ARR_NDIM(v))
04755             SRF_RETURN_DONE(funcctx);
04756 
04757         /*
04758          * switch to memory context appropriate for multiple function calls
04759          */
04760         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
04761         fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
04762 
04763         lb = ARR_LBOUND(v);
04764         dimv = ARR_DIMS(v);
04765 
04766         fctx->lower = lb[reqdim - 1];
04767         fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
04768         fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
04769 
04770         funcctx->user_fctx = fctx;
04771 
04772         MemoryContextSwitchTo(oldcontext);
04773     }
04774 
04775     funcctx = SRF_PERCALL_SETUP();
04776 
04777     fctx = funcctx->user_fctx;
04778 
04779     if (fctx->lower <= fctx->upper)
04780     {
04781         if (!fctx->reverse)
04782             SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
04783         else
04784             SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
04785     }
04786     else
04787         /* done when there are no more elements left */
04788         SRF_RETURN_DONE(funcctx);
04789 }
04790 
04791 /*
04792  * generate_subscripts_nodir
04793  *      Implements the 2-argument version of generate_subscripts
04794  */
04795 Datum
04796 generate_subscripts_nodir(PG_FUNCTION_ARGS)
04797 {
04798     /* just call the other one -- it can handle both cases */
04799     return generate_subscripts(fcinfo);
04800 }
04801 
04802 /*
04803  * array_fill_with_lower_bounds
04804  *      Create and fill array with defined lower bounds.
04805  */
04806 Datum
04807 array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
04808 {
04809     ArrayType  *dims;
04810     ArrayType  *lbs;
04811     ArrayType  *result;
04812     Oid         elmtype;
04813     Datum       value;
04814     bool        isnull;
04815 
04816     if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
04817         ereport(ERROR,
04818                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
04819                errmsg("dimension array or low bound array cannot be null")));
04820 
04821     dims = PG_GETARG_ARRAYTYPE_P(1);
04822     lbs = PG_GETARG_ARRAYTYPE_P(2);
04823 
04824     if (!PG_ARGISNULL(0))
04825     {
04826         value = PG_GETARG_DATUM(0);
04827         isnull = false;
04828     }
04829     else
04830     {
04831         value = 0;
04832         isnull = true;
04833     }
04834 
04835     elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
04836     if (!OidIsValid(elmtype))
04837         elog(ERROR, "could not determine data type of input");
04838 
04839     result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
04840     PG_RETURN_ARRAYTYPE_P(result);
04841 }
04842 
04843 /*
04844  * array_fill
04845  *      Create and fill array with default lower bounds.
04846  */
04847 Datum
04848 array_fill(PG_FUNCTION_ARGS)
04849 {
04850     ArrayType  *dims;
04851     ArrayType  *result;
04852     Oid         elmtype;
04853     Datum       value;
04854     bool        isnull;
04855 
04856     if (PG_ARGISNULL(1))
04857         ereport(ERROR,
04858                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
04859                errmsg("dimension array or low bound array cannot be null")));
04860 
04861     dims = PG_GETARG_ARRAYTYPE_P(1);
04862 
04863     if (!PG_ARGISNULL(0))
04864     {
04865         value = PG_GETARG_DATUM(0);
04866         isnull = false;
04867     }
04868     else
04869     {
04870         value = 0;
04871         isnull = true;
04872     }
04873 
04874     elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
04875     if (!OidIsValid(elmtype))
04876         elog(ERROR, "could not determine data type of input");
04877 
04878     result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
04879     PG_RETURN_ARRAYTYPE_P(result);
04880 }
04881 
04882 static ArrayType *
04883 create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
04884                       Oid elmtype, int dataoffset)
04885 {
04886     ArrayType  *result;
04887 
04888     result = (ArrayType *) palloc0(nbytes);
04889     SET_VARSIZE(result, nbytes);
04890     result->ndim = ndims;
04891     result->dataoffset = dataoffset;
04892     result->elemtype = elmtype;
04893     memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
04894     memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
04895 
04896     return result;
04897 }
04898 
04899 static ArrayType *
04900 array_fill_internal(ArrayType *dims, ArrayType *lbs,
04901                     Datum value, bool isnull, Oid elmtype,
04902                     FunctionCallInfo fcinfo)
04903 {
04904     ArrayType  *result;
04905     int        *dimv;
04906     int        *lbsv;
04907     int         ndims;
04908     int         nitems;
04909     int         deflbs[MAXDIM];
04910     int16       elmlen;
04911     bool        elmbyval;
04912     char        elmalign;
04913     ArrayMetaState *my_extra;
04914 
04915     /*
04916      * Params checks
04917      */
04918     if (ARR_NDIM(dims) != 1)
04919         ereport(ERROR,
04920                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
04921                  errmsg("wrong number of array subscripts"),
04922                  errdetail("Dimension array must be one dimensional.")));
04923 
04924     if (ARR_LBOUND(dims)[0] != 1)
04925         ereport(ERROR,
04926                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
04927                  errmsg("wrong range of array subscripts"),
04928                  errdetail("Lower bound of dimension array must be one.")));
04929 
04930     if (array_contains_nulls(dims))
04931         ereport(ERROR,
04932                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
04933                  errmsg("dimension values cannot be null")));
04934 
04935     dimv = (int *) ARR_DATA_PTR(dims);
04936     ndims = ARR_DIMS(dims)[0];
04937 
04938     if (ndims < 0)              /* we do allow zero-dimension arrays */
04939         ereport(ERROR,
04940                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
04941                  errmsg("invalid number of dimensions: %d", ndims)));
04942     if (ndims > MAXDIM)
04943         ereport(ERROR,
04944                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
04945                  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
04946                         ndims, MAXDIM)));
04947 
04948     if (lbs != NULL)
04949     {
04950         if (ARR_NDIM(lbs) != 1)
04951             ereport(ERROR,
04952                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
04953                      errmsg("wrong number of array subscripts"),
04954                      errdetail("Dimension array must be one dimensional.")));
04955 
04956         if (ARR_LBOUND(lbs)[0] != 1)
04957             ereport(ERROR,
04958                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
04959                      errmsg("wrong range of array subscripts"),
04960                   errdetail("Lower bound of dimension array must be one.")));
04961 
04962         if (array_contains_nulls(lbs))
04963             ereport(ERROR,
04964                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
04965                      errmsg("dimension values cannot be null")));
04966 
04967         if (ARR_DIMS(lbs)[0] != ndims)
04968             ereport(ERROR,
04969                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
04970                      errmsg("wrong number of array subscripts"),
04971                      errdetail("Low bound array has different size than dimensions array.")));
04972 
04973         lbsv = (int *) ARR_DATA_PTR(lbs);
04974     }
04975     else
04976     {
04977         int         i;
04978 
04979         for (i = 0; i < MAXDIM; i++)
04980             deflbs[i] = 1;
04981 
04982         lbsv = deflbs;
04983     }
04984 
04985     /* fast track for empty array */
04986     if (ndims == 0)
04987         return construct_empty_array(elmtype);
04988 
04989     nitems = ArrayGetNItems(ndims, dimv);
04990 
04991     /*
04992      * We arrange to look up info about element type only once per series of
04993      * calls, assuming the element type doesn't change underneath us.
04994      */
04995     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
04996     if (my_extra == NULL)
04997     {
04998         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
04999                                                       sizeof(ArrayMetaState));
05000         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
05001         my_extra->element_type = InvalidOid;
05002     }
05003 
05004     if (my_extra->element_type != elmtype)
05005     {
05006         /* Get info about element type */
05007         get_typlenbyvalalign(elmtype,
05008                              &my_extra->typlen,
05009                              &my_extra->typbyval,
05010                              &my_extra->typalign);
05011         my_extra->element_type = elmtype;
05012     }
05013 
05014     elmlen = my_extra->typlen;
05015     elmbyval = my_extra->typbyval;
05016     elmalign = my_extra->typalign;
05017 
05018     /* compute required space */
05019     if (!isnull)
05020     {
05021         int         i;
05022         char       *p;
05023         int         nbytes;
05024         int         totbytes;
05025 
05026         /* make sure data is not toasted */
05027         if (elmlen == -1)
05028             value = PointerGetDatum(PG_DETOAST_DATUM(value));
05029 
05030         nbytes = att_addlength_datum(0, elmlen, value);
05031         nbytes = att_align_nominal(nbytes, elmalign);
05032         Assert(nbytes > 0);
05033 
05034         totbytes = nbytes * nitems;
05035 
05036         /* check for overflow of multiplication or total request */
05037         if (totbytes / nbytes != nitems ||
05038             !AllocSizeIsValid(totbytes))
05039             ereport(ERROR,
05040                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
05041                      errmsg("array size exceeds the maximum allowed (%d)",
05042                             (int) MaxAllocSize)));
05043 
05044         /*
05045          * This addition can't overflow, but it might cause us to go past
05046          * MaxAllocSize.  We leave it to palloc to complain in that case.
05047          */
05048         totbytes += ARR_OVERHEAD_NONULLS(ndims);
05049 
05050         result = create_array_envelope(ndims, dimv, lbsv, totbytes,
05051                                        elmtype, 0);
05052 
05053         p = ARR_DATA_PTR(result);
05054         for (i = 0; i < nitems; i++)
05055             p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
05056     }
05057     else
05058     {
05059         int         nbytes;
05060         int         dataoffset;
05061 
05062         dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
05063         nbytes = dataoffset;
05064 
05065         result = create_array_envelope(ndims, dimv, lbsv, nbytes,
05066                                        elmtype, dataoffset);
05067 
05068         /* create_array_envelope already zeroed the bitmap, so we're done */
05069     }
05070 
05071     return result;
05072 }
05073 
05074 
05075 /*
05076  * UNNEST
05077  */
05078 Datum
05079 array_unnest(PG_FUNCTION_ARGS)
05080 {
05081     typedef struct
05082     {
05083         ArrayType  *arr;
05084         int         nextelem;
05085         int         numelems;
05086         char       *elemdataptr;    /* this moves with nextelem */
05087         bits8      *arraynullsptr;      /* this does not */
05088         int16       elmlen;
05089         bool        elmbyval;
05090         char        elmalign;
05091     } array_unnest_fctx;
05092 
05093     FuncCallContext *funcctx;
05094     array_unnest_fctx *fctx;
05095     MemoryContext oldcontext;
05096 
05097     /* stuff done only on the first call of the function */
05098     if (SRF_IS_FIRSTCALL())
05099     {
05100         ArrayType  *arr;
05101 
05102         /* create a function context for cross-call persistence */
05103         funcctx = SRF_FIRSTCALL_INIT();
05104 
05105         /*
05106          * switch to memory context appropriate for multiple function calls
05107          */
05108         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
05109 
05110         /*
05111          * Get the array value and detoast if needed.  We can't do this
05112          * earlier because if we have to detoast, we want the detoasted copy
05113          * to be in multi_call_memory_ctx, so it will go away when we're done
05114          * and not before.  (If no detoast happens, we assume the originally
05115          * passed array will stick around till then.)
05116          */
05117         arr = PG_GETARG_ARRAYTYPE_P(0);
05118 
05119         /* allocate memory for user context */
05120         fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
05121 
05122         /* initialize state */
05123         fctx->arr = arr;
05124         fctx->nextelem = 0;
05125         fctx->numelems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
05126 
05127         fctx->elemdataptr = ARR_DATA_PTR(arr);
05128         fctx->arraynullsptr = ARR_NULLBITMAP(arr);
05129 
05130         get_typlenbyvalalign(ARR_ELEMTYPE(arr),
05131                              &fctx->elmlen,
05132                              &fctx->elmbyval,
05133                              &fctx->elmalign);
05134 
05135         funcctx->user_fctx = fctx;
05136         MemoryContextSwitchTo(oldcontext);
05137     }
05138 
05139     /* stuff done on every call of the function */
05140     funcctx = SRF_PERCALL_SETUP();
05141     fctx = funcctx->user_fctx;
05142 
05143     if (fctx->nextelem < fctx->numelems)
05144     {
05145         int         offset = fctx->nextelem++;
05146         Datum       elem;
05147 
05148         /*
05149          * Check for NULL array element
05150          */
05151         if (array_get_isnull(fctx->arraynullsptr, offset))
05152         {
05153             fcinfo->isnull = true;
05154             elem = (Datum) 0;
05155             /* elemdataptr does not move */
05156         }
05157         else
05158         {
05159             /*
05160              * OK, get the element
05161              */
05162             char       *ptr = fctx->elemdataptr;
05163 
05164             fcinfo->isnull = false;
05165             elem = ArrayCast(ptr, fctx->elmbyval, fctx->elmlen);
05166 
05167             /*
05168              * Advance elemdataptr over it
05169              */
05170             ptr = att_addlength_pointer(ptr, fctx->elmlen, ptr);
05171             ptr = (char *) att_align_nominal(ptr, fctx->elmalign);
05172             fctx->elemdataptr = ptr;
05173         }
05174 
05175         SRF_RETURN_NEXT(funcctx, elem);
05176     }
05177     else
05178     {
05179         /* do when there is no more left */
05180         SRF_RETURN_DONE(funcctx);
05181     }
05182 }
05183 
05184 
05185 /*
05186  * array_replace/array_remove support
05187  *
05188  * Find all array entries matching (not distinct from) search/search_isnull,
05189  * and delete them if remove is true, else replace them with
05190  * replace/replace_isnull.  Comparisons are done using the specified
05191  * collation.  fcinfo is passed only for caching purposes.
05192  */
05193 static ArrayType *
05194 array_replace_internal(ArrayType *array,
05195                        Datum search, bool search_isnull,
05196                        Datum replace, bool replace_isnull,
05197                        bool remove, Oid collation,
05198                        FunctionCallInfo fcinfo)
05199 {
05200     ArrayType  *result;
05201     Oid         element_type;
05202     Datum      *values;
05203     bool       *nulls;
05204     int        *dim;
05205     int         ndim;
05206     int         nitems,
05207                 nresult;
05208     int         i;
05209     int32       nbytes = 0;
05210     int32       dataoffset;
05211     bool        hasnulls;
05212     int         typlen;
05213     bool        typbyval;
05214     char        typalign;
05215     char       *arraydataptr;
05216     bits8      *bitmap;
05217     int         bitmask;
05218     bool        changed = false;
05219     TypeCacheEntry *typentry;
05220     FunctionCallInfoData locfcinfo;
05221 
05222     element_type = ARR_ELEMTYPE(array);
05223     ndim = ARR_NDIM(array);
05224     dim = ARR_DIMS(array);
05225     nitems = ArrayGetNItems(ndim, dim);
05226 
05227     /* Return input array unmodified if it is empty */
05228     if (nitems <= 0)
05229         return array;
05230 
05231     /*
05232      * We can't remove elements from multi-dimensional arrays, since the
05233      * result might not be rectangular.
05234      */
05235     if (remove && ndim > 1)
05236         ereport(ERROR,
05237                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
05238                  errmsg("removing elements from multidimensional arrays is not supported")));
05239 
05240     /*
05241      * We arrange to look up the equality function only once per series of
05242      * calls, assuming the element type doesn't change underneath us.
05243      */
05244     typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
05245     if (typentry == NULL ||
05246         typentry->type_id != element_type)
05247     {
05248         typentry = lookup_type_cache(element_type,
05249                                      TYPECACHE_EQ_OPR_FINFO);
05250         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
05251             ereport(ERROR,
05252                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
05253                      errmsg("could not identify an equality operator for type %s",
05254                             format_type_be(element_type))));
05255         fcinfo->flinfo->fn_extra = (void *) typentry;
05256     }
05257     typlen = typentry->typlen;
05258     typbyval = typentry->typbyval;
05259     typalign = typentry->typalign;
05260 
05261     /*
05262      * Detoast values if they are toasted.  The replacement value must be
05263      * detoasted for insertion into the result array, while detoasting the
05264      * search value only once saves cycles.
05265      */
05266     if (typlen == -1)
05267     {
05268         if (!search_isnull)
05269             search = PointerGetDatum(PG_DETOAST_DATUM(search));
05270         if (!replace_isnull)
05271             replace = PointerGetDatum(PG_DETOAST_DATUM(replace));
05272     }
05273 
05274     /* Prepare to apply the comparison operator */
05275     InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
05276                              collation, NULL, NULL);
05277 
05278     /* Allocate temporary arrays for new values */
05279     values = (Datum *) palloc(nitems * sizeof(Datum));
05280     nulls = (bool *) palloc(nitems * sizeof(bool));
05281 
05282     /* Loop over source data */
05283     arraydataptr = ARR_DATA_PTR(array);
05284     bitmap = ARR_NULLBITMAP(array);
05285     bitmask = 1;
05286     hasnulls = false;
05287     nresult = 0;
05288 
05289     for (i = 0; i < nitems; i++)
05290     {
05291         Datum       elt;
05292         bool        isNull;
05293         bool        oprresult;
05294         bool        skip = false;
05295 
05296         /* Get source element, checking for NULL */
05297         if (bitmap && (*bitmap & bitmask) == 0)
05298         {
05299             isNull = true;
05300             /* If searching for NULL, we have a match */
05301             if (search_isnull)
05302             {
05303                 if (remove)
05304                 {
05305                     skip = true;
05306                     changed = true;
05307                 }
05308                 else if (!replace_isnull)
05309                 {
05310                     values[nresult] = replace;
05311                     isNull = false;
05312                     changed = true;
05313                 }
05314             }
05315         }
05316         else
05317         {
05318             isNull = false;
05319             elt = fetch_att(arraydataptr, typbyval, typlen);
05320             arraydataptr = att_addlength_datum(arraydataptr, typlen, elt);
05321             arraydataptr = (char *) att_align_nominal(arraydataptr, typalign);
05322 
05323             if (search_isnull)
05324             {
05325                 /* no match possible, keep element */
05326                 values[nresult] = elt;
05327             }
05328             else
05329             {
05330                 /*
05331                  * Apply the operator to the element pair
05332                  */
05333                 locfcinfo.arg[0] = elt;
05334                 locfcinfo.arg[1] = search;
05335                 locfcinfo.argnull[0] = false;
05336                 locfcinfo.argnull[1] = false;
05337                 locfcinfo.isnull = false;
05338                 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
05339                 if (!oprresult)
05340                 {
05341                     /* no match, keep element */
05342                     values[nresult] = elt;
05343                 }
05344                 else
05345                 {
05346                     /* match, so replace or delete */
05347                     changed = true;
05348                     if (remove)
05349                         skip = true;
05350                     else
05351                     {
05352                         values[nresult] = replace;
05353                         isNull = replace_isnull;
05354                     }
05355                 }
05356             }
05357         }
05358 
05359         if (!skip)
05360         {
05361             nulls[nresult] = isNull;
05362             if (isNull)
05363                 hasnulls = true;
05364             else
05365             {
05366                 /* Update total result size */
05367                 nbytes = att_addlength_datum(nbytes, typlen, values[nresult]);
05368                 nbytes = att_align_nominal(nbytes, typalign);
05369                 /* check for overflow of total request */
05370                 if (!AllocSizeIsValid(nbytes))
05371                     ereport(ERROR,
05372                             (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
05373                              errmsg("array size exceeds the maximum allowed (%d)",
05374                                     (int) MaxAllocSize)));
05375             }
05376             nresult++;
05377         }
05378 
05379         /* advance bitmap pointer if any */
05380         if (bitmap)
05381         {
05382             bitmask <<= 1;
05383             if (bitmask == 0x100)
05384             {
05385                 bitmap++;
05386                 bitmask = 1;
05387             }
05388         }
05389     }
05390 
05391     /*
05392      * If not changed just return the original array
05393      */
05394     if (!changed)
05395     {
05396         pfree(values);
05397         pfree(nulls);
05398         return array;
05399     }
05400 
05401     /* Allocate and initialize the result array */
05402     if (hasnulls)
05403     {
05404         dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nresult);
05405         nbytes += dataoffset;
05406     }
05407     else
05408     {
05409         dataoffset = 0;         /* marker for no null bitmap */
05410         nbytes += ARR_OVERHEAD_NONULLS(ndim);
05411     }
05412     result = (ArrayType *) palloc0(nbytes);
05413     SET_VARSIZE(result, nbytes);
05414     result->ndim = ndim;
05415     result->dataoffset = dataoffset;
05416     result->elemtype = element_type;
05417     memcpy(ARR_DIMS(result), ARR_DIMS(array), 2 * ndim * sizeof(int));
05418 
05419     if (remove)
05420     {
05421         /* Adjust the result length */
05422         ARR_DIMS(result)[0] = nresult;
05423     }
05424 
05425     /* Insert data into result array */
05426     CopyArrayEls(result,
05427                  values, nulls, nresult,
05428                  typlen, typbyval, typalign,
05429                  false);
05430 
05431     pfree(values);
05432     pfree(nulls);
05433 
05434     return result;
05435 }
05436 
05437 /*
05438  * Remove any occurrences of an element from an array
05439  *
05440  * If used on a multi-dimensional array this will raise an error.
05441  */
05442 Datum
05443 array_remove(PG_FUNCTION_ARGS)
05444 {
05445     ArrayType  *array;
05446     Datum       search = PG_GETARG_DATUM(1);
05447     bool        search_isnull = PG_ARGISNULL(1);
05448 
05449     if (PG_ARGISNULL(0))
05450         PG_RETURN_NULL();
05451     array = PG_GETARG_ARRAYTYPE_P(0);
05452 
05453     array = array_replace_internal(array,
05454                                    search, search_isnull,
05455                                    (Datum) 0, true,
05456                                    true, PG_GET_COLLATION(),
05457                                    fcinfo);
05458     PG_RETURN_ARRAYTYPE_P(array);
05459 }
05460 
05461 /*
05462  * Replace any occurrences of an element in an array
05463  */
05464 Datum
05465 array_replace(PG_FUNCTION_ARGS)
05466 {
05467     ArrayType  *array;
05468     Datum       search = PG_GETARG_DATUM(1);
05469     bool        search_isnull = PG_ARGISNULL(1);
05470     Datum       replace = PG_GETARG_DATUM(2);
05471     bool        replace_isnull = PG_ARGISNULL(2);
05472 
05473     if (PG_ARGISNULL(0))
05474         PG_RETURN_NULL();
05475     array = PG_GETARG_ARRAYTYPE_P(0);
05476 
05477     array = array_replace_internal(array,
05478                                    search, search_isnull,
05479                                    replace, replace_isnull,
05480                                    false, PG_GET_COLLATION(),
05481                                    fcinfo);
05482     PG_RETURN_ARRAYTYPE_P(array);
05483 }