LCOV - code coverage report
Current view: top level - src/backend/utils/adt - array_userfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15beta1 Lines: 254 287 88.5 %
Date: 2022-05-18 02:09:37 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-2022, 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        1904 : 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        1904 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
      51        1904 :     if (my_extra == NULL)
      52             :     {
      53             :         my_extra = (ArrayMetaState *)
      54        1176 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
      55             :                                sizeof(ArrayMetaState));
      56        1176 :         my_extra->element_type = InvalidOid;
      57        1176 :         fcinfo->flinfo->fn_extra = my_extra;
      58             :     }
      59             : 
      60             :     /* Figure out which context we want the result in */
      61        1904 :     if (!AggCheckCallContext(fcinfo, &resultcxt))
      62        1784 :         resultcxt = CurrentMemoryContext;
      63             : 
      64             :     /* Now collect the array value */
      65        1904 :     if (!PG_ARGISNULL(argno))
      66             :     {
      67        1880 :         MemoryContext oldcxt = MemoryContextSwitchTo(resultcxt);
      68             : 
      69        1880 :         eah = PG_GETARG_EXPANDED_ARRAYX(argno, my_extra);
      70        1880 :         MemoryContextSwitchTo(oldcxt);
      71             :     }
      72             :     else
      73             :     {
      74             :         /* We have to look up the array type and element type */
      75          24 :         Oid         arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno);
      76             : 
      77          24 :         if (!OidIsValid(arr_typeid))
      78           0 :             ereport(ERROR,
      79             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      80             :                      errmsg("could not determine input data type")));
      81          24 :         element_type = get_element_type(arr_typeid);
      82          24 :         if (!OidIsValid(element_type))
      83           0 :             ereport(ERROR,
      84             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
      85             :                      errmsg("input data type is not an array")));
      86             : 
      87          24 :         eah = construct_empty_expanded_array(element_type,
      88             :                                              resultcxt,
      89             :                                              my_extra);
      90             :     }
      91             : 
      92        1904 :     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        1826 : 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        1826 :     eah = fetch_array_arg_replace_nulls(fcinfo, 0);
     113        1826 :     isNull = PG_ARGISNULL(1);
     114        1826 :     if (isNull)
     115           0 :         newelem = (Datum) 0;
     116             :     else
     117        1826 :         newelem = PG_GETARG_DATUM(1);
     118             : 
     119        1826 :     if (eah->ndims == 1)
     120             :     {
     121             :         /* append newelem */
     122        1410 :         lb = eah->lbound;
     123        1410 :         dimv = eah->dims;
     124             : 
     125             :         /* index of added elem is at lb[0] + (dimv[0] - 1) + 1 */
     126        1410 :         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         416 :     else if (eah->ndims == 0)
     132         416 :         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        1826 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     140             : 
     141        1826 :     result = array_set_element(EOHPGetRWDatum(&eah->hdr),
     142             :                                1, &indx, newelem, isNull,
     143        1826 :                                -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
     144             : 
     145        1826 :     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          78 : 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          78 :     isNull = PG_ARGISNULL(0);
     166          78 :     if (isNull)
     167           0 :         newelem = (Datum) 0;
     168             :     else
     169          78 :         newelem = PG_GETARG_DATUM(0);
     170          78 :     eah = fetch_array_arg_replace_nulls(fcinfo, 1);
     171             : 
     172          78 :     if (eah->ndims == 1)
     173             :     {
     174             :         /* prepend newelem */
     175          78 :         lb = eah->lbound;
     176          78 :         lb0 = lb[0];
     177             : 
     178          78 :         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          78 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     195             : 
     196          78 :     result = array_set_element(EOHPGetRWDatum(&eah->hdr),
     197             :                                1, &indx, newelem, isNull,
     198          78 :                                -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          78 :     if (eah->ndims == 1)
     203             :     {
     204             :         /* This is ok whether we've deconstructed or not */
     205          78 :         eah->lbound[0] = lb0;
     206             :     }
     207             : 
     208          78 :     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        2254 : 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        2254 :     if (PG_ARGISNULL(0))
     251             :     {
     252        1176 :         if (PG_ARGISNULL(1))
     253           0 :             PG_RETURN_NULL();
     254        1176 :         result = PG_GETARG_ARRAYTYPE_P(1);
     255        1176 :         PG_RETURN_ARRAYTYPE_P(result);
     256             :     }
     257        1078 :     if (PG_ARGISNULL(1))
     258             :     {
     259           0 :         result = PG_GETARG_ARRAYTYPE_P(0);
     260           0 :         PG_RETURN_ARRAYTYPE_P(result);
     261             :     }
     262             : 
     263        1078 :     v1 = PG_GETARG_ARRAYTYPE_P(0);
     264        1078 :     v2 = PG_GETARG_ARRAYTYPE_P(1);
     265             : 
     266        1078 :     element_type1 = ARR_ELEMTYPE(v1);
     267        1078 :     element_type2 = ARR_ELEMTYPE(v2);
     268             : 
     269             :     /* Check we have matching element types */
     270        1078 :     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        1078 :     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        1078 :     ndims1 = ARR_NDIM(v1);
     292        1078 :     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        1078 :     if (ndims1 == 0 && ndims2 > 0)
     301           0 :         PG_RETURN_ARRAYTYPE_P(v2);
     302             : 
     303        1078 :     if (ndims2 == 0)
     304          34 :         PG_RETURN_ARRAYTYPE_P(v1);
     305             : 
     306             :     /* the rest fall under rule 3, 4, or 5 */
     307        1044 :     if (ndims1 != ndims2 &&
     308          18 :         ndims1 != ndims2 - 1 &&
     309          12 :         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        1044 :     lbs1 = ARR_LBOUND(v1);
     319        1044 :     lbs2 = ARR_LBOUND(v2);
     320        1044 :     dims1 = ARR_DIMS(v1);
     321        1044 :     dims2 = ARR_DIMS(v2);
     322        1044 :     dat1 = ARR_DATA_PTR(v1);
     323        1044 :     dat2 = ARR_DATA_PTR(v2);
     324        1044 :     bitmap1 = ARR_NULLBITMAP(v1);
     325        1044 :     bitmap2 = ARR_NULLBITMAP(v2);
     326        1044 :     nitems1 = ArrayGetNItems(ndims1, dims1);
     327        1044 :     nitems2 = ArrayGetNItems(ndims2, dims2);
     328        1044 :     ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
     329        1044 :     ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
     330             : 
     331        1044 :     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        1026 :         ndims = ndims1;
     338        1026 :         dims = (int *) palloc(ndims * sizeof(int));
     339        1026 :         lbs = (int *) palloc(ndims * sizeof(int));
     340             : 
     341        1026 :         dims[0] = dims1[0] + dims2[0];
     342        1026 :         lbs[0] = lbs1[0];
     343             : 
     344        1038 :         for (i = 1; i < ndims; i++)
     345             :         {
     346          12 :             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          12 :             dims[i] = dims1[i];
     354          12 :             lbs[i] = lbs1[i];
     355             :         }
     356             :     }
     357          18 :     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           6 :         ndims = ndims2;
     364           6 :         dims = (int *) palloc(ndims * sizeof(int));
     365           6 :         lbs = (int *) palloc(ndims * sizeof(int));
     366           6 :         memcpy(dims, dims2, ndims * sizeof(int));
     367           6 :         memcpy(lbs, lbs2, ndims * sizeof(int));
     368             : 
     369             :         /* increment number of elements in outer array */
     370           6 :         dims[0] += 1;
     371             : 
     372             :         /* make sure the added element matches our existing elements */
     373          12 :         for (i = 0; i < ndims1; i++)
     374             :         {
     375           6 :             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          12 :         ndims = ndims1;
     392          12 :         dims = (int *) palloc(ndims * sizeof(int));
     393          12 :         lbs = (int *) palloc(ndims * sizeof(int));
     394          12 :         memcpy(dims, dims1, ndims * sizeof(int));
     395          12 :         memcpy(lbs, lbs1, ndims * sizeof(int));
     396             : 
     397             :         /* increment number of elements in outer array */
     398          12 :         dims[0] += 1;
     399             : 
     400             :         /* make sure the added element matches our existing elements */
     401          24 :         for (i = 0; i < ndims2; i++)
     402             :         {
     403          12 :             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        1044 :     nitems = ArrayGetNItems(ndims, dims);
     414        1044 :     ArrayCheckBounds(ndims, dims, lbs);
     415             : 
     416             :     /* build the result array */
     417        1044 :     ndatabytes = ndatabytes1 + ndatabytes2;
     418        1044 :     if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
     419             :     {
     420           0 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
     421           0 :         nbytes = ndatabytes + dataoffset;
     422             :     }
     423             :     else
     424             :     {
     425        1044 :         dataoffset = 0;         /* marker for no null bitmap */
     426        1044 :         nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
     427             :     }
     428        1044 :     result = (ArrayType *) palloc0(nbytes);
     429        1044 :     SET_VARSIZE(result, nbytes);
     430        1044 :     result->ndim = ndims;
     431        1044 :     result->dataoffset = dataoffset;
     432        1044 :     result->elemtype = element_type;
     433        1044 :     memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
     434        1044 :     memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
     435             :     /* data area is arg1 then arg2 */
     436        1044 :     memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
     437        1044 :     memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
     438             :     /* handle the null bitmap if needed */
     439        1044 :     if (ARR_HASNULL(result))
     440             :     {
     441           0 :         array_bitmap_copy(ARR_NULLBITMAP(result), 0,
     442             :                           bitmap1, 0,
     443             :                           nitems1);
     444           0 :         array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
     445             :                           bitmap2, 0,
     446             :                           nitems2);
     447             :     }
     448             : 
     449        1044 :     PG_RETURN_ARRAYTYPE_P(result);
     450             : }
     451             : 
     452             : 
     453             : /*
     454             :  * ARRAY_AGG(anynonarray) aggregate function
     455             :  */
     456             : Datum
     457     1644416 : array_agg_transfn(PG_FUNCTION_ARGS)
     458             : {
     459     1644416 :     Oid         arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
     460             :     MemoryContext aggcontext;
     461             :     ArrayBuildState *state;
     462             :     Datum       elem;
     463             : 
     464     1644416 :     if (arg1_typeid == InvalidOid)
     465           0 :         ereport(ERROR,
     466             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     467             :                  errmsg("could not determine input data type")));
     468             : 
     469             :     /*
     470             :      * Note: we do not need a run-time check about whether arg1_typeid is a
     471             :      * valid array element type, because the parser would have verified that
     472             :      * while resolving the input/result types of this polymorphic aggregate.
     473             :      */
     474             : 
     475     1644416 :     if (!AggCheckCallContext(fcinfo, &aggcontext))
     476             :     {
     477             :         /* cannot be called directly because of internal-type argument */
     478           0 :         elog(ERROR, "array_agg_transfn called in non-aggregate context");
     479             :     }
     480             : 
     481     1644416 :     if (PG_ARGISNULL(0))
     482       90824 :         state = initArrayResult(arg1_typeid, aggcontext, false);
     483             :     else
     484     1553592 :         state = (ArrayBuildState *) PG_GETARG_POINTER(0);
     485             : 
     486     1644416 :     elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
     487             : 
     488     1644416 :     state = accumArrayResult(state,
     489             :                              elem,
     490     1644416 :                              PG_ARGISNULL(1),
     491             :                              arg1_typeid,
     492             :                              aggcontext);
     493             : 
     494             :     /*
     495             :      * The transition type for array_agg() is declared to be "internal", which
     496             :      * is a pass-by-value type the same size as a pointer.  So we can safely
     497             :      * pass the ArrayBuildState pointer through nodeAgg.c's machinations.
     498             :      */
     499     1644416 :     PG_RETURN_POINTER(state);
     500             : }
     501             : 
     502             : Datum
     503       97368 : array_agg_finalfn(PG_FUNCTION_ARGS)
     504             : {
     505             :     Datum       result;
     506             :     ArrayBuildState *state;
     507             :     int         dims[1];
     508             :     int         lbs[1];
     509             : 
     510             :     /* cannot be called directly because of internal-type argument */
     511             :     Assert(AggCheckCallContext(fcinfo, NULL));
     512             : 
     513       97368 :     state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
     514             : 
     515       97368 :     if (state == NULL)
     516        6522 :         PG_RETURN_NULL();       /* returns null iff no input values */
     517             : 
     518       90846 :     dims[0] = state->nelems;
     519       90846 :     lbs[0] = 1;
     520             : 
     521             :     /*
     522             :      * Make the result.  We cannot release the ArrayBuildState because
     523             :      * sometimes aggregate final functions are re-executed.  Rather, it is
     524             :      * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
     525             :      * so.
     526             :      */
     527       90846 :     result = makeMdArrayResult(state, 1, dims, lbs,
     528             :                                CurrentMemoryContext,
     529             :                                false);
     530             : 
     531       90846 :     PG_RETURN_DATUM(result);
     532             : }
     533             : 
     534             : /*
     535             :  * ARRAY_AGG(anyarray) aggregate function
     536             :  */
     537             : Datum
     538         150 : array_agg_array_transfn(PG_FUNCTION_ARGS)
     539             : {
     540         150 :     Oid         arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
     541             :     MemoryContext aggcontext;
     542             :     ArrayBuildStateArr *state;
     543             : 
     544         150 :     if (arg1_typeid == InvalidOid)
     545           0 :         ereport(ERROR,
     546             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     547             :                  errmsg("could not determine input data type")));
     548             : 
     549             :     /*
     550             :      * Note: we do not need a run-time check about whether arg1_typeid is a
     551             :      * valid array type, because the parser would have verified that while
     552             :      * resolving the input/result types of this polymorphic aggregate.
     553             :      */
     554             : 
     555         150 :     if (!AggCheckCallContext(fcinfo, &aggcontext))
     556             :     {
     557             :         /* cannot be called directly because of internal-type argument */
     558           0 :         elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
     559             :     }
     560             : 
     561             : 
     562         150 :     if (PG_ARGISNULL(0))
     563          60 :         state = initArrayResultArr(arg1_typeid, InvalidOid, aggcontext, false);
     564             :     else
     565          90 :         state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
     566             : 
     567         150 :     state = accumArrayResultArr(state,
     568             :                                 PG_GETARG_DATUM(1),
     569         150 :                                 PG_ARGISNULL(1),
     570             :                                 arg1_typeid,
     571             :                                 aggcontext);
     572             : 
     573             :     /*
     574             :      * The transition type for array_agg() is declared to be "internal", which
     575             :      * is a pass-by-value type the same size as a pointer.  So we can safely
     576             :      * pass the ArrayBuildStateArr pointer through nodeAgg.c's machinations.
     577             :      */
     578         132 :     PG_RETURN_POINTER(state);
     579             : }
     580             : 
     581             : Datum
     582          42 : array_agg_array_finalfn(PG_FUNCTION_ARGS)
     583             : {
     584             :     Datum       result;
     585             :     ArrayBuildStateArr *state;
     586             : 
     587             :     /* cannot be called directly because of internal-type argument */
     588             :     Assert(AggCheckCallContext(fcinfo, NULL));
     589             : 
     590          42 :     state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
     591             : 
     592          42 :     if (state == NULL)
     593           0 :         PG_RETURN_NULL();       /* returns null iff no input values */
     594             : 
     595             :     /*
     596             :      * Make the result.  We cannot release the ArrayBuildStateArr because
     597             :      * sometimes aggregate final functions are re-executed.  Rather, it is
     598             :      * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
     599             :      * so.
     600             :      */
     601          42 :     result = makeArrayResultArr(state, CurrentMemoryContext, false);
     602             : 
     603          42 :     PG_RETURN_DATUM(result);
     604             : }
     605             : 
     606             : /*-----------------------------------------------------------------------------
     607             :  * array_position, array_position_start :
     608             :  *          return the offset of a value in an array.
     609             :  *
     610             :  * IS NOT DISTINCT FROM semantics are used for comparisons.  Return NULL when
     611             :  * the value is not found.
     612             :  *-----------------------------------------------------------------------------
     613             :  */
     614             : Datum
     615         216 : array_position(PG_FUNCTION_ARGS)
     616             : {
     617         216 :     return array_position_common(fcinfo);
     618             : }
     619             : 
     620             : Datum
     621          18 : array_position_start(PG_FUNCTION_ARGS)
     622             : {
     623          18 :     return array_position_common(fcinfo);
     624             : }
     625             : 
     626             : /*
     627             :  * array_position_common
     628             :  *      Common code for array_position and array_position_start
     629             :  *
     630             :  * These are separate wrappers for the sake of opr_sanity regression test.
     631             :  * They are not strict so we have to test for null inputs explicitly.
     632             :  */
     633             : static Datum
     634         234 : array_position_common(FunctionCallInfo fcinfo)
     635             : {
     636             :     ArrayType  *array;
     637         234 :     Oid         collation = PG_GET_COLLATION();
     638             :     Oid         element_type;
     639             :     Datum       searched_element,
     640             :                 value;
     641             :     bool        isnull;
     642             :     int         position,
     643             :                 position_min;
     644         234 :     bool        found = false;
     645             :     TypeCacheEntry *typentry;
     646             :     ArrayMetaState *my_extra;
     647             :     bool        null_search;
     648             :     ArrayIterator array_iterator;
     649             : 
     650         234 :     if (PG_ARGISNULL(0))
     651           0 :         PG_RETURN_NULL();
     652             : 
     653         234 :     array = PG_GETARG_ARRAYTYPE_P(0);
     654         234 :     element_type = ARR_ELEMTYPE(array);
     655             : 
     656             :     /*
     657             :      * We refuse to search for elements in multi-dimensional arrays, since we
     658             :      * have no good way to report the element's location in the array.
     659             :      */
     660         234 :     if (ARR_NDIM(array) > 1)
     661           6 :         ereport(ERROR,
     662             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     663             :                  errmsg("searching for elements in multidimensional arrays is not supported")));
     664             : 
     665         228 :     if (PG_ARGISNULL(1))
     666             :     {
     667             :         /* fast return when the array doesn't have nulls */
     668          12 :         if (!array_contains_nulls(array))
     669           6 :             PG_RETURN_NULL();
     670           6 :         searched_element = (Datum) 0;
     671           6 :         null_search = true;
     672             :     }
     673             :     else
     674             :     {
     675         216 :         searched_element = PG_GETARG_DATUM(1);
     676         216 :         null_search = false;
     677             :     }
     678             : 
     679         222 :     position = (ARR_LBOUND(array))[0] - 1;
     680             : 
     681             :     /* figure out where to start */
     682         222 :     if (PG_NARGS() == 3)
     683             :     {
     684          18 :         if (PG_ARGISNULL(2))
     685           0 :             ereport(ERROR,
     686             :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     687             :                      errmsg("initial position must not be null")));
     688             : 
     689          18 :         position_min = PG_GETARG_INT32(2);
     690             :     }
     691             :     else
     692         204 :         position_min = (ARR_LBOUND(array))[0];
     693             : 
     694             :     /*
     695             :      * We arrange to look up type info for array_create_iterator only once per
     696             :      * series of calls, assuming the element type doesn't change underneath
     697             :      * us.
     698             :      */
     699         222 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     700         222 :     if (my_extra == NULL)
     701             :     {
     702          84 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     703             :                                                       sizeof(ArrayMetaState));
     704          84 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     705          84 :         my_extra->element_type = ~element_type;
     706             :     }
     707             : 
     708         222 :     if (my_extra->element_type != element_type)
     709             :     {
     710          84 :         get_typlenbyvalalign(element_type,
     711             :                              &my_extra->typlen,
     712             :                              &my_extra->typbyval,
     713             :                              &my_extra->typalign);
     714             : 
     715          84 :         typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
     716             : 
     717          84 :         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
     718           0 :             ereport(ERROR,
     719             :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
     720             :                      errmsg("could not identify an equality operator for type %s",
     721             :                             format_type_be(element_type))));
     722             : 
     723          84 :         my_extra->element_type = element_type;
     724          84 :         fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
     725          84 :                       fcinfo->flinfo->fn_mcxt);
     726             :     }
     727             : 
     728             :     /* Examine each array element until we find a match. */
     729         222 :     array_iterator = array_create_iterator(array, 0, my_extra);
     730         618 :     while (array_iterate(array_iterator, &value, &isnull))
     731             :     {
     732         576 :         position++;
     733             : 
     734             :         /* skip initial elements if caller requested so */
     735         576 :         if (position < position_min)
     736          78 :             continue;
     737             : 
     738             :         /*
     739             :          * Can't look at the array element's value if it's null; but if we
     740             :          * search for null, we have a hit and are done.
     741             :          */
     742         498 :         if (isnull || null_search)
     743             :         {
     744          42 :             if (isnull && null_search)
     745             :             {
     746           6 :                 found = true;
     747           6 :                 break;
     748             :             }
     749             :             else
     750          36 :                 continue;
     751             :         }
     752             : 
     753             :         /* not nulls, so run the operator */
     754         456 :         if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
     755             :                                            searched_element, value)))
     756             :         {
     757         174 :             found = true;
     758         174 :             break;
     759             :         }
     760             :     }
     761             : 
     762         222 :     array_free_iterator(array_iterator);
     763             : 
     764             :     /* Avoid leaking memory when handed toasted input */
     765         222 :     PG_FREE_IF_COPY(array, 0);
     766             : 
     767         222 :     if (!found)
     768          42 :         PG_RETURN_NULL();
     769             : 
     770         180 :     PG_RETURN_INT32(position);
     771             : }
     772             : 
     773             : /*-----------------------------------------------------------------------------
     774             :  * array_positions :
     775             :  *          return an array of positions of a value in an array.
     776             :  *
     777             :  * IS NOT DISTINCT FROM semantics are used for comparisons.  Returns NULL when
     778             :  * the input array is NULL.  When the value is not found in the array, returns
     779             :  * an empty array.
     780             :  *
     781             :  * This is not strict so we have to test for null inputs explicitly.
     782             :  *-----------------------------------------------------------------------------
     783             :  */
     784             : Datum
     785          60 : array_positions(PG_FUNCTION_ARGS)
     786             : {
     787             :     ArrayType  *array;
     788          60 :     Oid         collation = PG_GET_COLLATION();
     789             :     Oid         element_type;
     790             :     Datum       searched_element,
     791             :                 value;
     792             :     bool        isnull;
     793             :     int         position;
     794             :     TypeCacheEntry *typentry;
     795             :     ArrayMetaState *my_extra;
     796             :     bool        null_search;
     797             :     ArrayIterator array_iterator;
     798          60 :     ArrayBuildState *astate = NULL;
     799             : 
     800          60 :     if (PG_ARGISNULL(0))
     801          12 :         PG_RETURN_NULL();
     802             : 
     803          48 :     array = PG_GETARG_ARRAYTYPE_P(0);
     804          48 :     element_type = ARR_ELEMTYPE(array);
     805             : 
     806          48 :     position = (ARR_LBOUND(array))[0] - 1;
     807             : 
     808             :     /*
     809             :      * We refuse to search for elements in multi-dimensional arrays, since we
     810             :      * have no good way to report the element's location in the array.
     811             :      */
     812          48 :     if (ARR_NDIM(array) > 1)
     813           6 :         ereport(ERROR,
     814             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     815             :                  errmsg("searching for elements in multidimensional arrays is not supported")));
     816             : 
     817          42 :     astate = initArrayResult(INT4OID, CurrentMemoryContext, false);
     818             : 
     819          42 :     if (PG_ARGISNULL(1))
     820             :     {
     821             :         /* fast return when the array doesn't have nulls */
     822          12 :         if (!array_contains_nulls(array))
     823           6 :             PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
     824           6 :         searched_element = (Datum) 0;
     825           6 :         null_search = true;
     826             :     }
     827             :     else
     828             :     {
     829          30 :         searched_element = PG_GETARG_DATUM(1);
     830          30 :         null_search = false;
     831             :     }
     832             : 
     833             :     /*
     834             :      * We arrange to look up type info for array_create_iterator only once per
     835             :      * series of calls, assuming the element type doesn't change underneath
     836             :      * us.
     837             :      */
     838          36 :     my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     839          36 :     if (my_extra == NULL)
     840             :     {
     841          30 :         fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     842             :                                                       sizeof(ArrayMetaState));
     843          30 :         my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
     844          30 :         my_extra->element_type = ~element_type;
     845             :     }
     846             : 
     847          36 :     if (my_extra->element_type != element_type)
     848             :     {
     849          30 :         get_typlenbyvalalign(element_type,
     850             :                              &my_extra->typlen,
     851             :                              &my_extra->typbyval,
     852             :                              &my_extra->typalign);
     853             : 
     854          30 :         typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
     855             : 
     856          30 :         if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
     857           0 :             ereport(ERROR,
     858             :                     (errcode(ERRCODE_UNDEFINED_FUNCTION),
     859             :                      errmsg("could not identify an equality operator for type %s",
     860             :                             format_type_be(element_type))));
     861             : 
     862          30 :         my_extra->element_type = element_type;
     863          30 :         fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
     864          30 :                       fcinfo->flinfo->fn_mcxt);
     865             :     }
     866             : 
     867             :     /*
     868             :      * Accumulate each array position iff the element matches the given
     869             :      * element.
     870             :      */
     871          36 :     array_iterator = array_create_iterator(array, 0, my_extra);
     872         816 :     while (array_iterate(array_iterator, &value, &isnull))
     873             :     {
     874         780 :         position += 1;
     875             : 
     876             :         /*
     877             :          * Can't look at the array element's value if it's null; but if we
     878             :          * search for null, we have a hit.
     879             :          */
     880         780 :         if (isnull || null_search)
     881             :         {
     882          72 :             if (isnull && null_search)
     883             :                 astate =
     884          12 :                     accumArrayResult(astate, Int32GetDatum(position), false,
     885             :                                      INT4OID, CurrentMemoryContext);
     886             : 
     887          72 :             continue;
     888             :         }
     889             : 
     890             :         /* not nulls, so run the operator */
     891         708 :         if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
     892             :                                            searched_element, value)))
     893             :             astate =
     894          90 :                 accumArrayResult(astate, Int32GetDatum(position), false,
     895             :                                  INT4OID, CurrentMemoryContext);
     896             :     }
     897             : 
     898          36 :     array_free_iterator(array_iterator);
     899             : 
     900             :     /* Avoid leaking memory when handed toasted input */
     901          36 :     PG_FREE_IF_COPY(array, 0);
     902             : 
     903          36 :     PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
     904             : }

Generated by: LCOV version 1.14