LCOV - code coverage report
Current view: top level - src/backend/utils/adt - array_userfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12beta2 Lines: 257 289 88.9 %
Date: 2019-06-18 07:06:57 Functions: 12 12 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * array_userfuncs.c
       4             :  *    Misc user-visible array support functions
       5             :  *
       6             :  * Copyright (c) 2003-2019, PostgreSQL Global Development Group
       7             :  *
       8             :  * IDENTIFICATION
       9             :  *    src/backend/utils/adt/array_userfuncs.c
      10             :  *
      11             :  *-------------------------------------------------------------------------
      12             :  */
      13             : #include "postgres.h"
      14             : 
      15             : #include "catalog/pg_type.h"
      16             : #include "common/int.h"
      17             : #include "utils/array.h"
      18             : #include "utils/builtins.h"
      19             : #include "utils/lsyscache.h"
      20             : #include "utils/typcache.h"
      21             : 
      22             : 
      23             : static Datum array_position_common(FunctionCallInfo fcinfo);
      24             : 
      25             : 
      26             : /*
      27             :  * fetch_array_arg_replace_nulls
      28             :  *
      29             :  * Fetch an array-valued argument in expanded form; if it's null, construct an
      30             :  * empty array value of the proper data type.  Also cache basic element type
      31             :  * information in fn_extra.
      32             :  *
      33             :  * Caution: if the input is a read/write pointer, this returns the input
      34             :  * argument; so callers must be sure that their changes are "safe", that is
      35             :  * they cannot leave the array in a corrupt state.
      36             :  *
      37             :  * If we're being called as an aggregate function, make sure any newly-made
      38             :  * expanded array is allocated in the aggregate state context, so as to save
      39             :  * copying operations.
      40             :  */
      41             : static ExpandedArrayHeader *
      42        1184 : fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno)
      43             : {
      44             :     ExpandedArrayHeader *eah;
      45             :     Oid         element_type;
      46             :     ArrayMetaState *my_extra;
      47             :     MemoryContext resultcxt;
      48             : 
      49             :     /* If first time through, create datatype cache struct */
      50        1184 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
      51        1184 :     if (my_extra == NULL)
      52             :     {
      53         688 :         my_extra = (ArrayMetaState *)
      54         688 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
      55             :                                sizeof(ArrayMetaState));
      56         688 :         my_extra->element_type = InvalidOid;
      57         688 :         fcinfo->flinfo->fn_extra = my_extra;
      58             :     }
      59             : 
      60             :     /* Figure out which context we want the result in */
      61        1184 :     if (!AggCheckCallContext(fcinfo, &resultcxt))
      62        1024 :         resultcxt = CurrentMemoryContext;
      63             : 
      64             :     /* Now collect the array value */
      65        1184 :     if (!PG_ARGISNULL(argno))
      66             :     {
      67        1168 :         MemoryContext oldcxt = MemoryContextSwitchTo(resultcxt);
      68             : 
      69        1168 :         eah = PG_GETARG_EXPANDED_ARRAYX(argno, my_extra);
      70        1168 :         MemoryContextSwitchTo(oldcxt);
      71             :     }
      72             :     else
      73             :     {
      74             :         /* We have to look up the array type and element type */
      75          16 :         Oid         arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno);
      76             : 
      77          16 :         if (!OidIsValid(arr_typeid))
      78           0 :             ereport(ERROR,
      79             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      80             :                      errmsg("could not determine input data type")));
      81          16 :         element_type = get_element_type(arr_typeid);
      82          16 :         if (!OidIsValid(element_type))
      83           0 :             ereport(ERROR,
      84             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
      85             :                      errmsg("input data type is not an array")));
      86             : 
      87          16 :         eah = construct_empty_expanded_array(element_type,
      88             :                                              resultcxt,
      89             :                                              my_extra);
      90             :     }
      91             : 
      92        1184 :     return eah;
      93             : }
      94             : 
      95             : /*-----------------------------------------------------------------------------
      96             :  * array_append :
      97             :  *      push an element onto the end of a one-dimensional array
      98             :  *----------------------------------------------------------------------------
      99             :  */
     100             : Datum
     101        1160 : array_append(PG_FUNCTION_ARGS)
     102             : {
     103             :     ExpandedArrayHeader *eah;
     104             :     Datum       newelem;
     105             :     bool        isNull;
     106             :     Datum       result;
     107             :     int        *dimv,
     108             :                *lb;
     109             :     int         indx;
     110             :     ArrayMetaState *my_extra;
     111             : 
     112        1160 :     eah = fetch_array_arg_replace_nulls(fcinfo, 0);
     113        1160 :     isNull = PG_ARGISNULL(1);
     114        1160 :     if (isNull)
     115           0 :         newelem = (Datum) 0;
     116             :     else
     117        1160 :         newelem = PG_GETARG_DATUM(1);
     118             : 
     119        1160 :     if (eah->ndims == 1)
     120             :     {
     121             :         /* append newelem */
     122         868 :         lb = eah->lbound;
     123         868 :         dimv = eah->dims;
     124             : 
     125             :         /* index of added elem is at lb[0] + (dimv[0] - 1) + 1 */
     126         868 :         if (pg_add_s32_overflow(lb[0], dimv[0], &indx))
     127           0 :             ereport(ERROR,
     128             :                     (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     129             :                      errmsg("integer out of range")));
     130             :     }
     131         292 :     else if (eah->ndims == 0)
     132         292 :         indx = 1;
     133             :     else
     134           0 :         ereport(ERROR,
     135             :                 (errcode(ERRCODE_DATA_EXCEPTION),
     136             :                  errmsg("argument must be empty or one-dimensional array")));
     137             : 
     138             :     /* Perform element insertion */
     139        1160 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     140             : 
     141        3480 :     result = array_set_element(EOHPGetRWDatum(&eah->hdr),
     142             :                                1, &indx, newelem, isNull,
     143        3480 :                                -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
     144             : 
     145        1160 :     PG_RETURN_DATUM(result);
     146             : }
     147             : 
     148             : /*-----------------------------------------------------------------------------
     149             :  * array_prepend :
     150             :  *      push an element onto the front of a one-dimensional array
     151             :  *----------------------------------------------------------------------------
     152             :  */
     153             : Datum
     154          24 : array_prepend(PG_FUNCTION_ARGS)
     155             : {
     156             :     ExpandedArrayHeader *eah;
     157             :     Datum       newelem;
     158             :     bool        isNull;
     159             :     Datum       result;
     160             :     int        *lb;
     161             :     int         indx;
     162             :     int         lb0;
     163             :     ArrayMetaState *my_extra;
     164             : 
     165          24 :     isNull = PG_ARGISNULL(0);
     166          24 :     if (isNull)
     167           0 :         newelem = (Datum) 0;
     168             :     else
     169          24 :         newelem = PG_GETARG_DATUM(0);
     170          24 :     eah = fetch_array_arg_replace_nulls(fcinfo, 1);
     171             : 
     172          24 :     if (eah->ndims == 1)
     173             :     {
     174             :         /* prepend newelem */
     175          24 :         lb = eah->lbound;
     176          24 :         lb0 = lb[0];
     177             : 
     178          24 :         if (pg_sub_s32_overflow(lb0, 1, &indx))
     179           0 :             ereport(ERROR,
     180             :                     (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     181             :                      errmsg("integer out of range")));
     182             :     }
     183           0 :     else if (eah->ndims == 0)
     184             :     {
     185           0 :         indx = 1;
     186           0 :         lb0 = 1;
     187             :     }
     188             :     else
     189           0 :         ereport(ERROR,
     190             :                 (errcode(ERRCODE_DATA_EXCEPTION),
     191             :                  errmsg("argument must be empty or one-dimensional array")));
     192             : 
     193             :     /* Perform element insertion */
     194          24 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     195             : 
     196          72 :     result = array_set_element(EOHPGetRWDatum(&eah->hdr),
     197             :                                1, &indx, newelem, isNull,
     198          72 :                                -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
     199             : 
     200             :     /* Readjust result's LB to match the input's, as expected for prepend */
     201             :     Assert(result == EOHPGetRWDatum(&eah->hdr));
     202          24 :     if (eah->ndims == 1)
     203             :     {
     204             :         /* This is ok whether we've deconstructed or not */
     205          24 :         eah->lbound[0] = lb0;
     206             :     }
     207             : 
     208          24 :     PG_RETURN_DATUM(result);
     209             : }
     210             : 
     211             : /*-----------------------------------------------------------------------------
     212             :  * array_cat :
     213             :  *      concatenate two nD arrays to form an nD array, or
     214             :  *      push an (n-1)D array onto the end of an nD array
     215             :  *----------------------------------------------------------------------------
     216             :  */
     217             : Datum
     218         704 : array_cat(PG_FUNCTION_ARGS)
     219             : {
     220             :     ArrayType  *v1,
     221             :                *v2;
     222             :     ArrayType  *result;
     223             :     int        *dims,
     224             :                *lbs,
     225             :                 ndims,
     226             :                 nitems,
     227             :                 ndatabytes,
     228             :                 nbytes;
     229             :     int        *dims1,
     230             :                *lbs1,
     231             :                 ndims1,
     232             :                 nitems1,
     233             :                 ndatabytes1;
     234             :     int        *dims2,
     235             :                *lbs2,
     236             :                 ndims2,
     237             :                 nitems2,
     238             :                 ndatabytes2;
     239             :     int         i;
     240             :     char       *dat1,
     241             :                *dat2;
     242             :     bits8      *bitmap1,
     243             :                *bitmap2;
     244             :     Oid         element_type;
     245             :     Oid         element_type1;
     246             :     Oid         element_type2;
     247             :     int32       dataoffset;
     248             : 
     249             :     /* Concatenating a null array is a no-op, just return the other input */
     250         704 :     if (PG_ARGISNULL(0))
     251             :     {
     252         626 :         if (PG_ARGISNULL(1))
     253           0 :             PG_RETURN_NULL();
     254         626 :         result = PG_GETARG_ARRAYTYPE_P(1);
     255         626 :         PG_RETURN_ARRAYTYPE_P(result);
     256             :     }
     257          78 :     if (PG_ARGISNULL(1))
     258             :     {
     259           0 :         result = PG_GETARG_ARRAYTYPE_P(0);
     260           0 :         PG_RETURN_ARRAYTYPE_P(result);
     261             :     }
     262             : 
     263          78 :     v1 = PG_GETARG_ARRAYTYPE_P(0);
     264          78 :     v2 = PG_GETARG_ARRAYTYPE_P(1);
     265             : 
     266          78 :     element_type1 = ARR_ELEMTYPE(v1);
     267          78 :     element_type2 = ARR_ELEMTYPE(v2);
     268             : 
     269             :     /* Check we have matching element types */
     270          78 :     if (element_type1 != element_type2)
     271           0 :         ereport(ERROR,
     272             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
     273             :                  errmsg("cannot concatenate incompatible arrays"),
     274             :                  errdetail("Arrays with element types %s and %s are not "
     275             :                            "compatible for concatenation.",
     276             :                            format_type_be(element_type1),
     277             :                            format_type_be(element_type2))));
     278             : 
     279             :     /* OK, use it */
     280          78 :     element_type = element_type1;
     281             : 
     282             :     /*----------
     283             :      * We must have one of the following combinations of inputs:
     284             :      * 1) one empty array, and one non-empty array
     285             :      * 2) both arrays empty
     286             :      * 3) two arrays with ndims1 == ndims2
     287             :      * 4) ndims1 == ndims2 - 1
     288             :      * 5) ndims1 == ndims2 + 1
     289             :      *----------
     290             :      */
     291          78 :     ndims1 = ARR_NDIM(v1);
     292          78 :     ndims2 = ARR_NDIM(v2);
     293             : 
     294             :     /*
     295             :      * short circuit - if one input array is empty, and the other is not, we
     296             :      * return the non-empty one as the result
     297             :      *
     298             :      * if both are empty, return the first one
     299             :      */
     300          78 :     if (ndims1 == 0 && ndims2 > 0)
     301           8 :         PG_RETURN_ARRAYTYPE_P(v2);
     302             : 
     303          70 :     if (ndims2 == 0)
     304          26 :         PG_RETURN_ARRAYTYPE_P(v1);
     305             : 
     306             :     /* the rest fall under rule 3, 4, or 5 */
     307          56 :     if (ndims1 != ndims2 &&
     308          20 :         ndims1 != ndims2 - 1 &&
     309           8 :         ndims1 != ndims2 + 1)
     310           0 :         ereport(ERROR,
     311             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     312             :                  errmsg("cannot concatenate incompatible arrays"),
     313             :                  errdetail("Arrays of %d and %d dimensions are not "
     314             :                            "compatible for concatenation.",
     315             :                            ndims1, ndims2)));
     316             : 
     317             :     /* get argument array details */
     318          44 :     lbs1 = ARR_LBOUND(v1);
     319          44 :     lbs2 = ARR_LBOUND(v2);
     320          44 :     dims1 = ARR_DIMS(v1);
     321          44 :     dims2 = ARR_DIMS(v2);
     322          44 :     dat1 = ARR_DATA_PTR(v1);
     323          44 :     dat2 = ARR_DATA_PTR(v2);
     324          44 :     bitmap1 = ARR_NULLBITMAP(v1);
     325          44 :     bitmap2 = ARR_NULLBITMAP(v2);
     326          44 :     nitems1 = ArrayGetNItems(ndims1, dims1);
     327          44 :     nitems2 = ArrayGetNItems(ndims2, dims2);
     328          44 :     ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
     329          44 :     ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
     330             : 
     331          44 :     if (ndims1 == ndims2)
     332             :     {
     333             :         /*
     334             :          * resulting array is made up of the elements (possibly arrays
     335             :          * themselves) of the input argument arrays
     336             :          */
     337          32 :         ndims = ndims1;
     338          32 :         dims = (int *) palloc(ndims * sizeof(int));
     339          32 :         lbs = (int *) palloc(ndims * sizeof(int));
     340             : 
     341          32 :         dims[0] = dims1[0] + dims2[0];
     342          32 :         lbs[0] = lbs1[0];
     343             : 
     344          40 :         for (i = 1; i < ndims; i++)
     345             :         {
     346           8 :             if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
     347           0 :                 ereport(ERROR,
     348             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     349             :                          errmsg("cannot concatenate incompatible arrays"),
     350             :                          errdetail("Arrays with differing element dimensions are "
     351             :                                    "not compatible for concatenation.")));
     352             : 
     353           8 :             dims[i] = dims1[i];
     354           8 :             lbs[i] = lbs1[i];
     355             :         }
     356             :     }
     357          12 :     else if (ndims1 == ndims2 - 1)
     358             :     {
     359             :         /*
     360             :          * resulting array has the second argument as the outer array, with
     361             :          * the first argument inserted at the front of the outer dimension
     362             :          */
     363           4 :         ndims = ndims2;
     364           4 :         dims = (int *) palloc(ndims * sizeof(int));
     365           4 :         lbs = (int *) palloc(ndims * sizeof(int));
     366           4 :         memcpy(dims, dims2, ndims * sizeof(int));
     367           4 :         memcpy(lbs, lbs2, ndims * sizeof(int));
     368             : 
     369             :         /* increment number of elements in outer array */
     370           4 :         dims[0] += 1;
     371             : 
     372             :         /* make sure the added element matches our existing elements */
     373           8 :         for (i = 0; i < ndims1; i++)
     374             :         {
     375           4 :             if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
     376           0 :                 ereport(ERROR,
     377             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     378             :                          errmsg("cannot concatenate incompatible arrays"),
     379             :                          errdetail("Arrays with differing dimensions are not "
     380             :                                    "compatible for concatenation.")));
     381             :         }
     382             :     }
     383             :     else
     384             :     {
     385             :         /*
     386             :          * (ndims1 == ndims2 + 1)
     387             :          *
     388             :          * resulting array has the first argument as the outer array, with the
     389             :          * second argument appended to the end of the outer dimension
     390             :          */
     391           8 :         ndims = ndims1;
     392           8 :         dims = (int *) palloc(ndims * sizeof(int));
     393           8 :         lbs = (int *) palloc(ndims * sizeof(int));
     394           8 :         memcpy(dims, dims1, ndims * sizeof(int));
     395           8 :         memcpy(lbs, lbs1, ndims * sizeof(int));
     396             : 
     397             :         /* increment number of elements in outer array */
     398           8 :         dims[0] += 1;
     399             : 
     400             :         /* make sure the added element matches our existing elements */
     401          16 :         for (i = 0; i < ndims2; i++)
     402             :         {
     403           8 :             if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
     404           0 :                 ereport(ERROR,
     405             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     406             :                          errmsg("cannot concatenate incompatible arrays"),
     407             :                          errdetail("Arrays with differing dimensions are not "
     408             :                                    "compatible for concatenation.")));
     409             :         }
     410             :     }
     411             : 
     412             :     /* Do this mainly for overflow checking */
     413          44 :     nitems = ArrayGetNItems(ndims, dims);
     414             : 
     415             :     /* build the result array */
     416          44 :     ndatabytes = ndatabytes1 + ndatabytes2;
     417          44 :     if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
     418             :     {
     419           0 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
     420           0 :         nbytes = ndatabytes + dataoffset;
     421             :     }
     422             :     else
     423             :     {
     424          44 :         dataoffset = 0;         /* marker for no null bitmap */
     425          44 :         nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
     426             :     }
     427          44 :     result = (ArrayType *) palloc0(nbytes);
     428          44 :     SET_VARSIZE(result, nbytes);
     429          44 :     result->ndim = ndims;
     430          44 :     result->dataoffset = dataoffset;
     431          44 :     result->elemtype = element_type;
     432          44 :     memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
     433          44 :     memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
     434             :     /* data area is arg1 then arg2 */
     435          44 :     memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
     436          44 :     memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
     437             :     /* handle the null bitmap if needed */
     438          44 :     if (ARR_HASNULL(result))
     439             :     {
     440           0 :         array_bitmap_copy(ARR_NULLBITMAP(result), 0,
     441             :                           bitmap1, 0,
     442             :                           nitems1);
     443           0 :         array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
     444             :                           bitmap2, 0,
     445             :                           nitems2);
     446             :     }
     447             : 
     448          44 :     PG_RETURN_ARRAYTYPE_P(result);
     449             : }
     450             : 
     451             : 
     452             : /*
     453             :  * ARRAY_AGG(anynonarray) aggregate function
     454             :  */
     455             : Datum
     456      448874 : array_agg_transfn(PG_FUNCTION_ARGS)
     457             : {
     458      448874 :     Oid         arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
     459             :     MemoryContext aggcontext;
     460             :     ArrayBuildState *state;
     461             :     Datum       elem;
     462             : 
     463      448874 :     if (arg1_typeid == InvalidOid)
     464           0 :         ereport(ERROR,
     465             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     466             :                  errmsg("could not determine input data type")));
     467             : 
     468             :     /*
     469             :      * Note: we do not need a run-time check about whether arg1_typeid is a
     470             :      * valid array element type, because the parser would have verified that
     471             :      * while resolving the input/result types of this polymorphic aggregate.
     472             :      */
     473             : 
     474      448874 :     if (!AggCheckCallContext(fcinfo, &aggcontext))
     475             :     {
     476             :         /* cannot be called directly because of internal-type argument */
     477           0 :         elog(ERROR, "array_agg_transfn called in non-aggregate context");
     478             :     }
     479             : 
     480      448874 :     if (PG_ARGISNULL(0))
     481       54122 :         state = initArrayResult(arg1_typeid, aggcontext, false);
     482             :     else
     483      394752 :         state = (ArrayBuildState *) PG_GETARG_POINTER(0);
     484             : 
     485      448874 :     elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
     486             : 
     487      897748 :     state = accumArrayResult(state,
     488             :                              elem,
     489      448874 :                              PG_ARGISNULL(1),
     490             :                              arg1_typeid,
     491             :                              aggcontext);
     492             : 
     493             :     /*
     494             :      * The transition type for array_agg() is declared to be "internal", which
     495             :      * is a pass-by-value type the same size as a pointer.  So we can safely
     496             :      * pass the ArrayBuildState pointer through nodeAgg.c's machinations.
     497             :      */
     498      448874 :     PG_RETURN_POINTER(state);
     499             : }
     500             : 
     501             : Datum
     502     1140960 : array_agg_finalfn(PG_FUNCTION_ARGS)
     503             : {
     504             :     Datum       result;
     505             :     ArrayBuildState *state;
     506             :     int         dims[1];
     507             :     int         lbs[1];
     508             : 
     509             :     /* cannot be called directly because of internal-type argument */
     510             :     Assert(AggCheckCallContext(fcinfo, NULL));
     511             : 
     512     1140960 :     state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
     513             : 
     514     1140960 :     if (state == NULL)
     515     1086822 :         PG_RETURN_NULL();       /* returns null iff no input values */
     516             : 
     517       54138 :     dims[0] = state->nelems;
     518       54138 :     lbs[0] = 1;
     519             : 
     520             :     /*
     521             :      * Make the result.  We cannot release the ArrayBuildState because
     522             :      * sometimes aggregate final functions are re-executed.  Rather, it is
     523             :      * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
     524             :      * so.
     525             :      */
     526       54138 :     result = makeMdArrayResult(state, 1, dims, lbs,
     527             :                                CurrentMemoryContext,
     528             :                                false);
     529             : 
     530       54138 :     PG_RETURN_DATUM(result);
     531             : }
     532             : 
     533             : /*
     534             :  * ARRAY_AGG(anyarray) aggregate function
     535             :  */
     536             : Datum
     537         100 : array_agg_array_transfn(PG_FUNCTION_ARGS)
     538             : {
     539         100 :     Oid         arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
     540             :     MemoryContext aggcontext;
     541             :     ArrayBuildStateArr *state;
     542             : 
     543         100 :     if (arg1_typeid == InvalidOid)
     544           0 :         ereport(ERROR,
     545             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     546             :                  errmsg("could not determine input data type")));
     547             : 
     548             :     /*
     549             :      * Note: we do not need a run-time check about whether arg1_typeid is a
     550             :      * valid array type, because the parser would have verified that while
     551             :      * resolving the input/result types of this polymorphic aggregate.
     552             :      */
     553             : 
     554         100 :     if (!AggCheckCallContext(fcinfo, &aggcontext))
     555             :     {
     556             :         /* cannot be called directly because of internal-type argument */
     557           0 :         elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
     558             :     }
     559             : 
     560             : 
     561         100 :     if (PG_ARGISNULL(0))
     562          40 :         state = initArrayResultArr(arg1_typeid, InvalidOid, aggcontext, false);
     563             :     else
     564          60 :         state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
     565             : 
     566         200 :     state = accumArrayResultArr(state,
     567             :                                 PG_GETARG_DATUM(1),
     568         100 :                                 PG_ARGISNULL(1),
     569             :                                 arg1_typeid,
     570             :                                 aggcontext);
     571             : 
     572             :     /*
     573             :      * The transition type for array_agg() is declared to be "internal", which
     574             :      * is a pass-by-value type the same size as a pointer.  So we can safely
     575             :      * pass the ArrayBuildStateArr pointer through nodeAgg.c's machinations.
     576             :      */
     577          88 :     PG_RETURN_POINTER(state);
     578             : }
     579             : 
     580             : Datum
     581          28 : array_agg_array_finalfn(PG_FUNCTION_ARGS)
     582             : {
     583             :     Datum       result;
     584             :     ArrayBuildStateArr *state;
     585             : 
     586             :     /* cannot be called directly because of internal-type argument */
     587             :     Assert(AggCheckCallContext(fcinfo, NULL));
     588             : 
     589          28 :     state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
     590             : 
     591          28 :     if (state == NULL)
     592           0 :         PG_RETURN_NULL();       /* returns null iff no input values */
     593             : 
     594             :     /*
     595             :      * Make the result.  We cannot release the ArrayBuildStateArr because
     596             :      * sometimes aggregate final functions are re-executed.  Rather, it is
     597             :      * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
     598             :      * so.
     599             :      */
     600          28 :     result = makeArrayResultArr(state, CurrentMemoryContext, false);
     601             : 
     602          28 :     PG_RETURN_DATUM(result);
     603             : }
     604             : 
     605             : /*-----------------------------------------------------------------------------
     606             :  * array_position, array_position_start :
     607             :  *          return the offset of a value in an array.
     608             :  *
     609             :  * IS NOT DISTINCT FROM semantics are used for comparisons.  Return NULL when
     610             :  * the value is not found.
     611             :  *-----------------------------------------------------------------------------
     612             :  */
     613             : Datum
     614          48 : array_position(PG_FUNCTION_ARGS)
     615             : {
     616          48 :     return array_position_common(fcinfo);
     617             : }
     618             : 
     619             : Datum
     620          12 : array_position_start(PG_FUNCTION_ARGS)
     621             : {
     622          12 :     return array_position_common(fcinfo);
     623             : }
     624             : 
     625             : /*
     626             :  * array_position_common
     627             :  *      Common code for array_position and array_position_start
     628             :  *
     629             :  * These are separate wrappers for the sake of opr_sanity regression test.
     630             :  * They are not strict so we have to test for null inputs explicitly.
     631             :  */
     632             : static Datum
     633          60 : array_position_common(FunctionCallInfo fcinfo)
     634             : {
     635             :     ArrayType  *array;
     636          60 :     Oid         collation = PG_GET_COLLATION();
     637             :     Oid         element_type;
     638             :     Datum       searched_element,
     639             :                 value;
     640             :     bool        isnull;
     641             :     int         position,
     642             :                 position_min;
     643          60 :     bool        found = false;
     644             :     TypeCacheEntry *typentry;
     645             :     ArrayMetaState *my_extra;
     646             :     bool        null_search;
     647             :     ArrayIterator array_iterator;
     648             : 
     649          60 :     if (PG_ARGISNULL(0))
     650           0 :         PG_RETURN_NULL();
     651             : 
     652          60 :     array = PG_GETARG_ARRAYTYPE_P(0);
     653          60 :     element_type = ARR_ELEMTYPE(array);
     654             : 
     655             :     /*
     656             :      * We refuse to search for elements in multi-dimensional arrays, since we
     657             :      * have no good way to report the element's location in the array.
     658             :      */
     659          60 :     if (ARR_NDIM(array) > 1)
     660           4 :         ereport(ERROR,
     661             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     662             :                  errmsg("searching for elements in multidimensional arrays is not supported")));
     663             : 
     664          56 :     if (PG_ARGISNULL(1))
     665             :     {
     666             :         /* fast return when the array doesn't have nulls */
     667           8 :         if (!array_contains_nulls(array))
     668           4 :             PG_RETURN_NULL();
     669           4 :         searched_element = (Datum) 0;
     670           4 :         null_search = true;
     671             :     }
     672             :     else
     673             :     {
     674          48 :         searched_element = PG_GETARG_DATUM(1);
     675          48 :         null_search = false;
     676             :     }
     677             : 
     678          52 :     position = (ARR_LBOUND(array))[0] - 1;
     679             : 
     680             :     /* figure out where to start */
     681          52 :     if (PG_NARGS() == 3)
     682             :     {
     683          12 :         if (PG_ARGISNULL(2))
     684           0 :             ereport(ERROR,
     685             :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     686             :                      errmsg("initial position must not be null")));
     687             : 
     688          12 :         position_min = PG_GETARG_INT32(2);
     689             :     }
     690             :     else
     691          40 :         position_min = (ARR_LBOUND(array))[0];
     692             : 
     693             :     /*
     694             :      * We arrange to look up type info for array_create_iterator only once per
     695             :      * series of calls, assuming the element type doesn't change underneath
     696             :      * us.
     697             :      */
     698          52 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     699          52 :     if (my_extra == NULL)
     700             :     {
     701          40 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     702             :                                                       sizeof(ArrayMetaState));
     703          40 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     704          40 :         my_extra->element_type = ~element_type;
     705             :     }
     706             : 
     707          52 :     if (my_extra->element_type != element_type)
     708             :     {
     709          40 :         get_typlenbyvalalign(element_type,
     710             :                              &my_extra->typlen,
     711             :                              &my_extra->typbyval,
     712             :                              &my_extra->typalign);
     713             : 
     714          40 :         typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
     715             : 
     716          40 :         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
     717           0 :             ereport(ERROR,
     718             :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
     719             :                      errmsg("could not identify an equality operator for type %s",
     720             :                             format_type_be(element_type))));
     721             : 
     722          40 :         my_extra->element_type = element_type;
     723          40 :         fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
     724          40 :                       fcinfo->flinfo->fn_mcxt);
     725             :     }
     726             : 
     727             :     /* Examine each array element until we find a match. */
     728          52 :     array_iterator = array_create_iterator(array, 0, my_extra);
     729          52 :     while (array_iterate(array_iterator, &value, &isnull))
     730             :     {
     731         216 :         position++;
     732             : 
     733             :         /* skip initial elements if caller requested so */
     734         216 :         if (position < position_min)
     735          52 :             continue;
     736             : 
     737             :         /*
     738             :          * Can't look at the array element's value if it's null; but if we
     739             :          * search for null, we have a hit and are done.
     740             :          */
     741         164 :         if (isnull || null_search)
     742             :         {
     743          28 :             if (isnull && null_search)
     744             :             {
     745           4 :                 found = true;
     746           4 :                 break;
     747             :             }
     748             :             else
     749          24 :                 continue;
     750             :         }
     751             : 
     752             :         /* not nulls, so run the operator */
     753         136 :         if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
     754             :                                            searched_element, value)))
     755             :         {
     756          44 :             found = true;
     757          44 :             break;
     758             :         }
     759             :     }
     760             : 
     761          52 :     array_free_iterator(array_iterator);
     762             : 
     763             :     /* Avoid leaking memory when handed toasted input */
     764          52 :     PG_FREE_IF_COPY(array, 0);
     765             : 
     766          52 :     if (!found)
     767           4 :         PG_RETURN_NULL();
     768             : 
     769          48 :     PG_RETURN_INT32(position);
     770             : }
     771             : 
     772             : /*-----------------------------------------------------------------------------
     773             :  * array_positions :
     774             :  *          return an array of positions of a value in an array.
     775             :  *
     776             :  * IS NOT DISTINCT FROM semantics are used for comparisons.  Returns NULL when
     777             :  * the input array is NULL.  When the value is not found in the array, returns
     778             :  * an empty array.
     779             :  *
     780             :  * This is not strict so we have to test for null inputs explicitly.
     781             :  *-----------------------------------------------------------------------------
     782             :  */
     783             : Datum
     784          40 : array_positions(PG_FUNCTION_ARGS)
     785             : {
     786             :     ArrayType  *array;
     787          40 :     Oid         collation = PG_GET_COLLATION();
     788             :     Oid         element_type;
     789             :     Datum       searched_element,
     790             :                 value;
     791             :     bool        isnull;
     792             :     int         position;
     793             :     TypeCacheEntry *typentry;
     794             :     ArrayMetaState *my_extra;
     795             :     bool        null_search;
     796             :     ArrayIterator array_iterator;
     797          40 :     ArrayBuildState *astate = NULL;
     798             : 
     799          40 :     if (PG_ARGISNULL(0))
     800           8 :         PG_RETURN_NULL();
     801             : 
     802          32 :     array = PG_GETARG_ARRAYTYPE_P(0);
     803          32 :     element_type = ARR_ELEMTYPE(array);
     804             : 
     805          32 :     position = (ARR_LBOUND(array))[0] - 1;
     806             : 
     807             :     /*
     808             :      * We refuse to search for elements in multi-dimensional arrays, since we
     809             :      * have no good way to report the element's location in the array.
     810             :      */
     811          32 :     if (ARR_NDIM(array) > 1)
     812           4 :         ereport(ERROR,
     813             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     814             :                  errmsg("searching for elements in multidimensional arrays is not supported")));
     815             : 
     816          28 :     astate = initArrayResult(INT4OID, CurrentMemoryContext, false);
     817             : 
     818          28 :     if (PG_ARGISNULL(1))
     819             :     {
     820             :         /* fast return when the array doesn't have nulls */
     821           8 :         if (!array_contains_nulls(array))
     822           4 :             PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
     823           4 :         searched_element = (Datum) 0;
     824           4 :         null_search = true;
     825             :     }
     826             :     else
     827             :     {
     828          20 :         searched_element = PG_GETARG_DATUM(1);
     829          20 :         null_search = false;
     830             :     }
     831             : 
     832             :     /*
     833             :      * We arrange to look up type info for array_create_iterator only once per
     834             :      * series of calls, assuming the element type doesn't change underneath
     835             :      * us.
     836             :      */
     837          24 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     838          24 :     if (my_extra == NULL)
     839             :     {
     840          20 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     841             :                                                       sizeof(ArrayMetaState));
     842          20 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     843          20 :         my_extra->element_type = ~element_type;
     844             :     }
     845             : 
     846          24 :     if (my_extra->element_type != element_type)
     847             :     {
     848          20 :         get_typlenbyvalalign(element_type,
     849             :                              &my_extra->typlen,
     850             :                              &my_extra->typbyval,
     851             :                              &my_extra->typalign);
     852             : 
     853          20 :         typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
     854             : 
     855          20 :         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
     856           0 :             ereport(ERROR,
     857             :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
     858             :                      errmsg("could not identify an equality operator for type %s",
     859             :                             format_type_be(element_type))));
     860             : 
     861          20 :         my_extra->element_type = element_type;
     862          20 :         fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
     863          20 :                       fcinfo->flinfo->fn_mcxt);
     864             :     }
     865             : 
     866             :     /*
     867             :      * Accumulate each array position iff the element matches the given
     868             :      * element.
     869             :      */
     870          24 :     array_iterator = array_create_iterator(array, 0, my_extra);
     871         568 :     while (array_iterate(array_iterator, &value, &isnull))
     872             :     {
     873         520 :         position += 1;
     874             : 
     875             :         /*
     876             :          * Can't look at the array element's value if it's null; but if we
     877             :          * search for null, we have a hit.
     878             :          */
     879         520 :         if (isnull || null_search)
     880             :         {
     881          48 :             if (isnull && null_search)
     882           8 :                 astate =
     883           8 :                     accumArrayResult(astate, Int32GetDatum(position), false,
     884             :                                      INT4OID, CurrentMemoryContext);
     885             : 
     886          48 :             continue;
     887             :         }
     888             : 
     889             :         /* not nulls, so run the operator */
     890         472 :         if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
     891             :                                            searched_element, value)))
     892          60 :             astate =
     893          60 :                 accumArrayResult(astate, Int32GetDatum(position), false,
     894             :                                  INT4OID, CurrentMemoryContext);
     895             :     }
     896             : 
     897          24 :     array_free_iterator(array_iterator);
     898             : 
     899             :     /* Avoid leaking memory when handed toasted input */
     900          24 :     PG_FREE_IF_COPY(array, 0);
     901             : 
     902          24 :     PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
     903             : }

Generated by: LCOV version 1.13