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

Generated by: LCOV version 1.11