LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_aggregate.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 199 257 77.4 %
Date: 2017-09-29 13:40:31 Functions: 2 2 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * pg_aggregate.c
       4             :  *    routines to support manipulation of the pg_aggregate relation
       5             :  *
       6             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/catalog/pg_aggregate.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/heapam.h"
      18             : #include "access/htup_details.h"
      19             : #include "catalog/dependency.h"
      20             : #include "catalog/indexing.h"
      21             : #include "catalog/pg_aggregate.h"
      22             : #include "catalog/pg_language.h"
      23             : #include "catalog/pg_operator.h"
      24             : #include "catalog/pg_proc.h"
      25             : #include "catalog/pg_proc_fn.h"
      26             : #include "catalog/pg_type.h"
      27             : #include "miscadmin.h"
      28             : #include "parser/parse_coerce.h"
      29             : #include "parser/parse_func.h"
      30             : #include "parser/parse_oper.h"
      31             : #include "utils/acl.h"
      32             : #include "utils/builtins.h"
      33             : #include "utils/lsyscache.h"
      34             : #include "utils/rel.h"
      35             : #include "utils/syscache.h"
      36             : 
      37             : 
      38             : static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
      39             :                     Oid variadicArgType,
      40             :                     Oid *rettype);
      41             : 
      42             : 
      43             : /*
      44             :  * AggregateCreate
      45             :  */
      46             : ObjectAddress
      47         115 : AggregateCreate(const char *aggName,
      48             :                 Oid aggNamespace,
      49             :                 char aggKind,
      50             :                 int numArgs,
      51             :                 int numDirectArgs,
      52             :                 oidvector *parameterTypes,
      53             :                 Datum allParameterTypes,
      54             :                 Datum parameterModes,
      55             :                 Datum parameterNames,
      56             :                 List *parameterDefaults,
      57             :                 Oid variadicArgType,
      58             :                 List *aggtransfnName,
      59             :                 List *aggfinalfnName,
      60             :                 List *aggcombinefnName,
      61             :                 List *aggserialfnName,
      62             :                 List *aggdeserialfnName,
      63             :                 List *aggmtransfnName,
      64             :                 List *aggminvtransfnName,
      65             :                 List *aggmfinalfnName,
      66             :                 bool finalfnExtraArgs,
      67             :                 bool mfinalfnExtraArgs,
      68             :                 List *aggsortopName,
      69             :                 Oid aggTransType,
      70             :                 int32 aggTransSpace,
      71             :                 Oid aggmTransType,
      72             :                 int32 aggmTransSpace,
      73             :                 const char *agginitval,
      74             :                 const char *aggminitval,
      75             :                 char proparallel)
      76             : {
      77             :     Relation    aggdesc;
      78             :     HeapTuple   tup;
      79             :     bool        nulls[Natts_pg_aggregate];
      80             :     Datum       values[Natts_pg_aggregate];
      81             :     Form_pg_proc proc;
      82             :     Oid         transfn;
      83         115 :     Oid         finalfn = InvalidOid;   /* can be omitted */
      84         115 :     Oid         combinefn = InvalidOid; /* can be omitted */
      85         115 :     Oid         serialfn = InvalidOid;  /* can be omitted */
      86         115 :     Oid         deserialfn = InvalidOid;    /* can be omitted */
      87         115 :     Oid         mtransfn = InvalidOid;  /* can be omitted */
      88         115 :     Oid         minvtransfn = InvalidOid;   /* can be omitted */
      89         115 :     Oid         mfinalfn = InvalidOid;  /* can be omitted */
      90         115 :     Oid         sortop = InvalidOid;    /* can be omitted */
      91         115 :     Oid        *aggArgTypes = parameterTypes->values;
      92             :     bool        hasPolyArg;
      93             :     bool        hasInternalArg;
      94         115 :     bool        mtransIsStrict = false;
      95             :     Oid         rettype;
      96             :     Oid         finaltype;
      97             :     Oid         fnArgs[FUNC_MAX_ARGS];
      98             :     int         nargs_transfn;
      99             :     int         nargs_finalfn;
     100             :     Oid         procOid;
     101             :     TupleDesc   tupDesc;
     102             :     int         i;
     103             :     ObjectAddress myself,
     104             :                 referenced;
     105             :     AclResult   aclresult;
     106             : 
     107             :     /* sanity checks (caller should have caught these) */
     108         115 :     if (!aggName)
     109           0 :         elog(ERROR, "no aggregate name supplied");
     110             : 
     111         115 :     if (!aggtransfnName)
     112           0 :         elog(ERROR, "aggregate must have a transition function");
     113             : 
     114         115 :     if (numDirectArgs < 0 || numDirectArgs > numArgs)
     115           0 :         elog(ERROR, "incorrect number of direct args for aggregate");
     116             : 
     117             :     /*
     118             :      * Aggregates can have at most FUNC_MAX_ARGS-1 args, else the transfn
     119             :      * and/or finalfn will be unrepresentable in pg_proc.  We must check now
     120             :      * to protect fixed-size arrays here and possibly in called functions.
     121             :      */
     122         115 :     if (numArgs < 0 || numArgs > FUNC_MAX_ARGS - 1)
     123           0 :         ereport(ERROR,
     124             :                 (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
     125             :                  errmsg_plural("aggregates cannot have more than %d argument",
     126             :                                "aggregates cannot have more than %d arguments",
     127             :                                FUNC_MAX_ARGS - 1,
     128             :                                FUNC_MAX_ARGS - 1)));
     129             : 
     130             :     /* check for polymorphic and INTERNAL arguments */
     131         115 :     hasPolyArg = false;
     132         115 :     hasInternalArg = false;
     133         226 :     for (i = 0; i < numArgs; i++)
     134             :     {
     135         111 :         if (IsPolymorphicType(aggArgTypes[i]))
     136          34 :             hasPolyArg = true;
     137          77 :         else if (aggArgTypes[i] == INTERNALOID)
     138           0 :             hasInternalArg = true;
     139             :     }
     140             : 
     141             :     /*
     142             :      * If transtype is polymorphic, must have polymorphic argument also; else
     143             :      * we will have no way to deduce the actual transtype.
     144             :      */
     145         115 :     if (IsPolymorphicType(aggTransType) && !hasPolyArg)
     146          18 :         ereport(ERROR,
     147             :                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     148             :                  errmsg("cannot determine transition data type"),
     149             :                  errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument.")));
     150             : 
     151             :     /*
     152             :      * Likewise for moving-aggregate transtype, if any
     153             :      */
     154          97 :     if (OidIsValid(aggmTransType) &&
     155           8 :         IsPolymorphicType(aggmTransType) && !hasPolyArg)
     156           0 :         ereport(ERROR,
     157             :                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     158             :                  errmsg("cannot determine transition data type"),
     159             :                  errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument.")));
     160             : 
     161             :     /*
     162             :      * An ordered-set aggregate that is VARIADIC must be VARIADIC ANY.  In
     163             :      * principle we could support regular variadic types, but it would make
     164             :      * things much more complicated because we'd have to assemble the correct
     165             :      * subsets of arguments into array values.  Since no standard aggregates
     166             :      * have use for such a case, we aren't bothering for now.
     167             :      */
     168          97 :     if (AGGKIND_IS_ORDERED_SET(aggKind) && OidIsValid(variadicArgType) &&
     169             :         variadicArgType != ANYOID)
     170           0 :         ereport(ERROR,
     171             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     172             :                  errmsg("a variadic ordered-set aggregate must use VARIADIC type ANY")));
     173             : 
     174             :     /*
     175             :      * If it's a hypothetical-set aggregate, there must be at least as many
     176             :      * direct arguments as aggregated ones, and the last N direct arguments
     177             :      * must match the aggregated ones in type.  (We have to check this again
     178             :      * when the aggregate is called, in case ANY is involved, but it makes
     179             :      * sense to reject the aggregate definition now if the declared arg types
     180             :      * don't match up.)  It's unconditionally OK if numDirectArgs == numArgs,
     181             :      * indicating that the grammar merged identical VARIADIC entries from both
     182             :      * lists.  Otherwise, if the agg is VARIADIC, then we had VARIADIC only on
     183             :      * the aggregated side, which is not OK.  Otherwise, insist on the last N
     184             :      * parameter types on each side matching exactly.
     185             :      */
     186          97 :     if (aggKind == AGGKIND_HYPOTHETICAL &&
     187             :         numDirectArgs < numArgs)
     188             :     {
     189           0 :         int         numAggregatedArgs = numArgs - numDirectArgs;
     190             : 
     191           0 :         if (OidIsValid(variadicArgType) ||
     192           0 :             numDirectArgs < numAggregatedArgs ||
     193           0 :             memcmp(aggArgTypes + (numDirectArgs - numAggregatedArgs),
     194           0 :                    aggArgTypes + numDirectArgs,
     195             :                    numAggregatedArgs * sizeof(Oid)) != 0)
     196           0 :             ereport(ERROR,
     197             :                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     198             :                      errmsg("a hypothetical-set aggregate must have direct arguments matching its aggregated arguments")));
     199             :     }
     200             : 
     201             :     /*
     202             :      * Find the transfn.  For ordinary aggs, it takes the transtype plus all
     203             :      * aggregate arguments.  For ordered-set aggs, it takes the transtype plus
     204             :      * all aggregated args, but not direct args.  However, we have to treat
     205             :      * specially the case where a trailing VARIADIC item is considered to
     206             :      * cover both direct and aggregated args.
     207             :      */
     208          97 :     if (AGGKIND_IS_ORDERED_SET(aggKind))
     209             :     {
     210           2 :         if (numDirectArgs < numArgs)
     211           1 :             nargs_transfn = numArgs - numDirectArgs + 1;
     212             :         else
     213             :         {
     214             :             /* special case with VARIADIC last arg */
     215           1 :             Assert(variadicArgType != InvalidOid);
     216           1 :             nargs_transfn = 2;
     217             :         }
     218           2 :         fnArgs[0] = aggTransType;
     219           2 :         memcpy(fnArgs + 1, aggArgTypes + (numArgs - (nargs_transfn - 1)),
     220             :                (nargs_transfn - 1) * sizeof(Oid));
     221             :     }
     222             :     else
     223             :     {
     224          95 :         nargs_transfn = numArgs + 1;
     225          95 :         fnArgs[0] = aggTransType;
     226          95 :         memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
     227             :     }
     228          97 :     transfn = lookup_agg_function(aggtransfnName, nargs_transfn,
     229             :                                   fnArgs, variadicArgType,
     230             :                                   &rettype);
     231             : 
     232             :     /*
     233             :      * Return type of transfn (possibly after refinement by
     234             :      * enforce_generic_type_consistency, if transtype isn't polymorphic) must
     235             :      * exactly match declared transtype.
     236             :      *
     237             :      * In the non-polymorphic-transtype case, it might be okay to allow a
     238             :      * rettype that's binary-coercible to transtype, but I'm not quite
     239             :      * convinced that it's either safe or useful.  When transtype is
     240             :      * polymorphic we *must* demand exact equality.
     241             :      */
     242          78 :     if (rettype != aggTransType)
     243           0 :         ereport(ERROR,
     244             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
     245             :                  errmsg("return type of transition function %s is not %s",
     246             :                         NameListToString(aggtransfnName),
     247             :                         format_type_be(aggTransType))));
     248             : 
     249          78 :     tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(transfn));
     250          78 :     if (!HeapTupleIsValid(tup))
     251           0 :         elog(ERROR, "cache lookup failed for function %u", transfn);
     252          78 :     proc = (Form_pg_proc) GETSTRUCT(tup);
     253             : 
     254             :     /*
     255             :      * If the transfn is strict and the initval is NULL, make sure first input
     256             :      * type and transtype are the same (or at least binary-compatible), so
     257             :      * that it's OK to use the first input value as the initial transValue.
     258             :      */
     259          78 :     if (proc->proisstrict && agginitval == NULL)
     260             :     {
     261          12 :         if (numArgs < 1 ||
     262           6 :             !IsBinaryCoercible(aggArgTypes[0], aggTransType))
     263           0 :             ereport(ERROR,
     264             :                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     265             :                      errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
     266             :     }
     267             : 
     268          78 :     ReleaseSysCache(tup);
     269             : 
     270             :     /* handle moving-aggregate transfn, if supplied */
     271          78 :     if (aggmtransfnName)
     272             :     {
     273             :         /*
     274             :          * The arguments are the same as for the regular transfn, except that
     275             :          * the transition data type might be different.  So re-use the fnArgs
     276             :          * values set up above, except for that one.
     277             :          */
     278           8 :         Assert(OidIsValid(aggmTransType));
     279           8 :         fnArgs[0] = aggmTransType;
     280             : 
     281           8 :         mtransfn = lookup_agg_function(aggmtransfnName, nargs_transfn,
     282             :                                        fnArgs, variadicArgType,
     283             :                                        &rettype);
     284             : 
     285             :         /* As above, return type must exactly match declared mtranstype. */
     286           8 :         if (rettype != aggmTransType)
     287           0 :             ereport(ERROR,
     288             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
     289             :                      errmsg("return type of transition function %s is not %s",
     290             :                             NameListToString(aggmtransfnName),
     291             :                             format_type_be(aggmTransType))));
     292             : 
     293           8 :         tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(mtransfn));
     294           8 :         if (!HeapTupleIsValid(tup))
     295           0 :             elog(ERROR, "cache lookup failed for function %u", mtransfn);
     296           8 :         proc = (Form_pg_proc) GETSTRUCT(tup);
     297             : 
     298             :         /*
     299             :          * If the mtransfn is strict and the minitval is NULL, check first
     300             :          * input type and mtranstype are binary-compatible.
     301             :          */
     302           8 :         if (proc->proisstrict && aggminitval == NULL)
     303             :         {
     304          10 :             if (numArgs < 1 ||
     305           5 :                 !IsBinaryCoercible(aggArgTypes[0], aggmTransType))
     306           0 :                 ereport(ERROR,
     307             :                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     308             :                          errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
     309             :         }
     310             : 
     311             :         /* Remember if mtransfn is strict; we may need this below */
     312           8 :         mtransIsStrict = proc->proisstrict;
     313             : 
     314           8 :         ReleaseSysCache(tup);
     315             :     }
     316             : 
     317             :     /* handle minvtransfn, if supplied */
     318          78 :     if (aggminvtransfnName)
     319             :     {
     320             :         /*
     321             :          * This must have the same number of arguments with the same types as
     322             :          * the forward transition function, so just re-use the fnArgs data.
     323             :          */
     324           8 :         Assert(aggmtransfnName);
     325             : 
     326           8 :         minvtransfn = lookup_agg_function(aggminvtransfnName, nargs_transfn,
     327             :                                           fnArgs, variadicArgType,
     328             :                                           &rettype);
     329             : 
     330             :         /* As above, return type must exactly match declared mtranstype. */
     331           8 :         if (rettype != aggmTransType)
     332           1 :             ereport(ERROR,
     333             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
     334             :                      errmsg("return type of inverse transition function %s is not %s",
     335             :                             NameListToString(aggminvtransfnName),
     336             :                             format_type_be(aggmTransType))));
     337             : 
     338           7 :         tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(minvtransfn));
     339           7 :         if (!HeapTupleIsValid(tup))
     340           0 :             elog(ERROR, "cache lookup failed for function %u", minvtransfn);
     341           7 :         proc = (Form_pg_proc) GETSTRUCT(tup);
     342             : 
     343             :         /*
     344             :          * We require the strictness settings of the forward and inverse
     345             :          * transition functions to agree.  This saves having to handle
     346             :          * assorted special cases at execution time.
     347             :          */
     348           7 :         if (proc->proisstrict != mtransIsStrict)
     349           1 :             ereport(ERROR,
     350             :                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     351             :                      errmsg("strictness of aggregate's forward and inverse transition functions must match")));
     352             : 
     353           6 :         ReleaseSysCache(tup);
     354             :     }
     355             : 
     356             :     /* handle finalfn, if supplied */
     357          76 :     if (aggfinalfnName)
     358             :     {
     359             :         /*
     360             :          * If finalfnExtraArgs is specified, the transfn takes the transtype
     361             :          * plus all args; otherwise, it just takes the transtype plus any
     362             :          * direct args.  (Non-direct args are useless at runtime, and are
     363             :          * actually passed as NULLs, but we may need them in the function
     364             :          * signature to allow resolution of a polymorphic agg's result type.)
     365             :          */
     366          29 :         Oid         ffnVariadicArgType = variadicArgType;
     367             : 
     368          29 :         fnArgs[0] = aggTransType;
     369          29 :         memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
     370          29 :         if (finalfnExtraArgs)
     371           2 :             nargs_finalfn = numArgs + 1;
     372             :         else
     373             :         {
     374          27 :             nargs_finalfn = numDirectArgs + 1;
     375          27 :             if (numDirectArgs < numArgs)
     376             :             {
     377             :                 /* variadic argument doesn't affect finalfn */
     378          23 :                 ffnVariadicArgType = InvalidOid;
     379             :             }
     380             :         }
     381             : 
     382          29 :         finalfn = lookup_agg_function(aggfinalfnName, nargs_finalfn,
     383             :                                       fnArgs, ffnVariadicArgType,
     384             :                                       &finaltype);
     385             : 
     386             :         /*
     387             :          * When finalfnExtraArgs is specified, the finalfn will certainly be
     388             :          * passed at least one null argument, so complain if it's strict.
     389             :          * Nothing bad would happen at runtime (you'd just get a null result),
     390             :          * but it's surely not what the user wants, so let's complain now.
     391             :          */
     392          27 :         if (finalfnExtraArgs && func_strict(finalfn))
     393           0 :             ereport(ERROR,
     394             :                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     395             :                      errmsg("final function with extra arguments must not be declared STRICT")));
     396             :     }
     397             :     else
     398             :     {
     399             :         /*
     400             :          * If no finalfn, aggregate result type is type of the state value
     401             :          */
     402          47 :         finaltype = aggTransType;
     403             :     }
     404          74 :     Assert(OidIsValid(finaltype));
     405             : 
     406             :     /* handle the combinefn, if supplied */
     407          74 :     if (aggcombinefnName)
     408             :     {
     409             :         Oid         combineType;
     410             : 
     411             :         /*
     412             :          * Combine function must have 2 argument, each of which is the trans
     413             :          * type
     414             :          */
     415           2 :         fnArgs[0] = aggTransType;
     416           2 :         fnArgs[1] = aggTransType;
     417             : 
     418           2 :         combinefn = lookup_agg_function(aggcombinefnName, 2, fnArgs,
     419             :                                         variadicArgType, &combineType);
     420             : 
     421             :         /* Ensure the return type matches the aggregates trans type */
     422           1 :         if (combineType != aggTransType)
     423           0 :             ereport(ERROR,
     424             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
     425             :                      errmsg("return type of combine function %s is not %s",
     426             :                             NameListToString(aggcombinefnName),
     427             :                             format_type_be(aggTransType))));
     428             : 
     429             :         /*
     430             :          * A combine function to combine INTERNAL states must accept nulls and
     431             :          * ensure that the returned state is in the correct memory context.
     432             :          */
     433           1 :         if (aggTransType == INTERNALOID && func_strict(combinefn))
     434           0 :             ereport(ERROR,
     435             :                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     436             :                      errmsg("combine function with transition type %s must not be declared STRICT",
     437             :                             format_type_be(aggTransType))));
     438             : 
     439             :     }
     440             : 
     441             :     /*
     442             :      * Validate the serialization function, if present.
     443             :      */
     444          73 :     if (aggserialfnName)
     445             :     {
     446           3 :         fnArgs[0] = INTERNALOID;
     447             : 
     448           3 :         serialfn = lookup_agg_function(aggserialfnName, 1,
     449             :                                        fnArgs, variadicArgType,
     450             :                                        &rettype);
     451             : 
     452           2 :         if (rettype != BYTEAOID)
     453           0 :             ereport(ERROR,
     454             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
     455             :                      errmsg("return type of serialization function %s is not %s",
     456             :                             NameListToString(aggserialfnName),
     457             :                             format_type_be(BYTEAOID))));
     458             :     }
     459             : 
     460             :     /*
     461             :      * Validate the deserialization function, if present.
     462             :      */
     463          72 :     if (aggdeserialfnName)
     464             :     {
     465           2 :         fnArgs[0] = BYTEAOID;
     466           2 :         fnArgs[1] = INTERNALOID;    /* dummy argument for type safety */
     467             : 
     468           2 :         deserialfn = lookup_agg_function(aggdeserialfnName, 2,
     469             :                                          fnArgs, variadicArgType,
     470             :                                          &rettype);
     471             : 
     472           1 :         if (rettype != INTERNALOID)
     473           0 :             ereport(ERROR,
     474             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
     475             :                      errmsg("return type of deserialization function %s is not %s",
     476             :                             NameListToString(aggdeserialfnName),
     477             :                             format_type_be(INTERNALOID))));
     478             :     }
     479             : 
     480             :     /*
     481             :      * If finaltype (i.e. aggregate return type) is polymorphic, inputs must
     482             :      * be polymorphic also, else parser will fail to deduce result type.
     483             :      * (Note: given the previous test on transtype and inputs, this cannot
     484             :      * happen, unless someone has snuck a finalfn definition into the catalogs
     485             :      * that itself violates the rule against polymorphic result with no
     486             :      * polymorphic input.)
     487             :      */
     488          71 :     if (IsPolymorphicType(finaltype) && !hasPolyArg)
     489           0 :         ereport(ERROR,
     490             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
     491             :                  errmsg("cannot determine result data type"),
     492             :                  errdetail("An aggregate returning a polymorphic type "
     493             :                            "must have at least one polymorphic argument.")));
     494             : 
     495             :     /*
     496             :      * Also, the return type can't be INTERNAL unless there's at least one
     497             :      * INTERNAL argument.  This is the same type-safety restriction we enforce
     498             :      * for regular functions, but at the level of aggregates.  We must test
     499             :      * this explicitly because we allow INTERNAL as the transtype.
     500             :      */
     501          71 :     if (finaltype == INTERNALOID && !hasInternalArg)
     502           0 :         ereport(ERROR,
     503             :                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     504             :                  errmsg("unsafe use of pseudo-type \"internal\""),
     505             :                  errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
     506             : 
     507             :     /*
     508             :      * If a moving-aggregate implementation is supplied, look up its finalfn
     509             :      * if any, and check that the implied aggregate result type matches the
     510             :      * plain implementation.
     511             :      */
     512          71 :     if (OidIsValid(aggmTransType))
     513             :     {
     514             :         /* handle finalfn, if supplied */
     515           6 :         if (aggmfinalfnName)
     516             :         {
     517             :             /*
     518             :              * The arguments are figured the same way as for the regular
     519             :              * finalfn, but using aggmTransType and mfinalfnExtraArgs.
     520             :              */
     521           0 :             Oid         ffnVariadicArgType = variadicArgType;
     522             : 
     523           0 :             fnArgs[0] = aggmTransType;
     524           0 :             memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
     525           0 :             if (mfinalfnExtraArgs)
     526           0 :                 nargs_finalfn = numArgs + 1;
     527             :             else
     528             :             {
     529           0 :                 nargs_finalfn = numDirectArgs + 1;
     530           0 :                 if (numDirectArgs < numArgs)
     531             :                 {
     532             :                     /* variadic argument doesn't affect finalfn */
     533           0 :                     ffnVariadicArgType = InvalidOid;
     534             :                 }
     535             :             }
     536             : 
     537           0 :             mfinalfn = lookup_agg_function(aggmfinalfnName, nargs_finalfn,
     538             :                                            fnArgs, ffnVariadicArgType,
     539             :                                            &rettype);
     540             : 
     541             :             /* As above, check strictness if mfinalfnExtraArgs is given */
     542           0 :             if (mfinalfnExtraArgs && func_strict(mfinalfn))
     543           0 :                 ereport(ERROR,
     544             :                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     545             :                          errmsg("final function with extra arguments must not be declared STRICT")));
     546             :         }
     547             :         else
     548             :         {
     549             :             /*
     550             :              * If no finalfn, aggregate result type is type of the state value
     551             :              */
     552           6 :             rettype = aggmTransType;
     553             :         }
     554           6 :         Assert(OidIsValid(rettype));
     555           6 :         if (rettype != finaltype)
     556           0 :             ereport(ERROR,
     557             :                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     558             :                      errmsg("moving-aggregate implementation returns type %s, but plain implementation returns type %s",
     559             :                             format_type_be(aggmTransType),
     560             :                             format_type_be(aggTransType))));
     561             :     }
     562             : 
     563             :     /* handle sortop, if supplied */
     564          71 :     if (aggsortopName)
     565             :     {
     566           0 :         if (numArgs != 1)
     567           0 :             ereport(ERROR,
     568             :                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     569             :                      errmsg("sort operator can only be specified for single-argument aggregates")));
     570           0 :         sortop = LookupOperName(NULL, aggsortopName,
     571             :                                 aggArgTypes[0], aggArgTypes[0],
     572             :                                 false, -1);
     573             :     }
     574             : 
     575             :     /*
     576             :      * permission checks on used types
     577             :      */
     578         143 :     for (i = 0; i < numArgs; i++)
     579             :     {
     580          72 :         aclresult = pg_type_aclcheck(aggArgTypes[i], GetUserId(), ACL_USAGE);
     581          72 :         if (aclresult != ACLCHECK_OK)
     582           0 :             aclcheck_error_type(aclresult, aggArgTypes[i]);
     583             :     }
     584             : 
     585          71 :     aclresult = pg_type_aclcheck(aggTransType, GetUserId(), ACL_USAGE);
     586          71 :     if (aclresult != ACLCHECK_OK)
     587           0 :         aclcheck_error_type(aclresult, aggTransType);
     588             : 
     589          71 :     if (OidIsValid(aggmTransType))
     590             :     {
     591           6 :         aclresult = pg_type_aclcheck(aggmTransType, GetUserId(), ACL_USAGE);
     592           6 :         if (aclresult != ACLCHECK_OK)
     593           0 :             aclcheck_error_type(aclresult, aggmTransType);
     594             :     }
     595             : 
     596          71 :     aclresult = pg_type_aclcheck(finaltype, GetUserId(), ACL_USAGE);
     597          71 :     if (aclresult != ACLCHECK_OK)
     598           0 :         aclcheck_error_type(aclresult, finaltype);
     599             : 
     600             : 
     601             :     /*
     602             :      * Everything looks okay.  Try to create the pg_proc entry for the
     603             :      * aggregate.  (This could fail if there's already a conflicting entry.)
     604             :      */
     605             : 
     606          71 :     myself = ProcedureCreate(aggName,
     607             :                              aggNamespace,
     608             :                              false, /* no replacement */
     609             :                              false, /* doesn't return a set */
     610             :                              finaltype, /* returnType */
     611             :                              GetUserId(),   /* proowner */
     612             :                              INTERNALlanguageId,    /* languageObjectId */
     613             :                              InvalidOid,    /* no validator */
     614             :                              "aggregate_dummy", /* placeholder proc */
     615             :                              NULL,  /* probin */
     616             :                              true,  /* isAgg */
     617             :                              false, /* isWindowFunc */
     618             :                              false, /* security invoker (currently not
     619             :                                      * definable for agg) */
     620             :                              false, /* isLeakProof */
     621             :                              false, /* isStrict (not needed for agg) */
     622             :                              PROVOLATILE_IMMUTABLE, /* volatility (not needed
     623             :                                                      * for agg) */
     624             :                              proparallel,
     625             :                              parameterTypes,    /* paramTypes */
     626             :                              allParameterTypes, /* allParamTypes */
     627             :                              parameterModes,    /* parameterModes */
     628             :                              parameterNames,    /* parameterNames */
     629             :                              parameterDefaults, /* parameterDefaults */
     630             :                              PointerGetDatum(NULL), /* trftypes */
     631             :                              PointerGetDatum(NULL), /* proconfig */
     632             :                              1, /* procost */
     633             :                              0);    /* prorows */
     634          70 :     procOid = myself.objectId;
     635             : 
     636             :     /*
     637             :      * Okay to create the pg_aggregate entry.
     638             :      */
     639             : 
     640             :     /* initialize nulls and values */
     641        1470 :     for (i = 0; i < Natts_pg_aggregate; i++)
     642             :     {
     643        1400 :         nulls[i] = false;
     644        1400 :         values[i] = (Datum) NULL;
     645             :     }
     646          70 :     values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
     647          70 :     values[Anum_pg_aggregate_aggkind - 1] = CharGetDatum(aggKind);
     648          70 :     values[Anum_pg_aggregate_aggnumdirectargs - 1] = Int16GetDatum(numDirectArgs);
     649          70 :     values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
     650          70 :     values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
     651          70 :     values[Anum_pg_aggregate_aggcombinefn - 1] = ObjectIdGetDatum(combinefn);
     652          70 :     values[Anum_pg_aggregate_aggserialfn - 1] = ObjectIdGetDatum(serialfn);
     653          70 :     values[Anum_pg_aggregate_aggdeserialfn - 1] = ObjectIdGetDatum(deserialfn);
     654          70 :     values[Anum_pg_aggregate_aggmtransfn - 1] = ObjectIdGetDatum(mtransfn);
     655          70 :     values[Anum_pg_aggregate_aggminvtransfn - 1] = ObjectIdGetDatum(minvtransfn);
     656          70 :     values[Anum_pg_aggregate_aggmfinalfn - 1] = ObjectIdGetDatum(mfinalfn);
     657          70 :     values[Anum_pg_aggregate_aggfinalextra - 1] = BoolGetDatum(finalfnExtraArgs);
     658          70 :     values[Anum_pg_aggregate_aggmfinalextra - 1] = BoolGetDatum(mfinalfnExtraArgs);
     659          70 :     values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
     660          70 :     values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
     661          70 :     values[Anum_pg_aggregate_aggtransspace - 1] = Int32GetDatum(aggTransSpace);
     662          70 :     values[Anum_pg_aggregate_aggmtranstype - 1] = ObjectIdGetDatum(aggmTransType);
     663          70 :     values[Anum_pg_aggregate_aggmtransspace - 1] = Int32GetDatum(aggmTransSpace);
     664          70 :     if (agginitval)
     665          42 :         values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval);
     666             :     else
     667          28 :         nulls[Anum_pg_aggregate_agginitval - 1] = true;
     668          70 :     if (aggminitval)
     669           2 :         values[Anum_pg_aggregate_aggminitval - 1] = CStringGetTextDatum(aggminitval);
     670             :     else
     671          68 :         nulls[Anum_pg_aggregate_aggminitval - 1] = true;
     672             : 
     673          70 :     aggdesc = heap_open(AggregateRelationId, RowExclusiveLock);
     674          70 :     tupDesc = aggdesc->rd_att;
     675             : 
     676          70 :     tup = heap_form_tuple(tupDesc, values, nulls);
     677          70 :     CatalogTupleInsert(aggdesc, tup);
     678             : 
     679          70 :     heap_close(aggdesc, RowExclusiveLock);
     680             : 
     681             :     /*
     682             :      * Create dependencies for the aggregate (above and beyond those already
     683             :      * made by ProcedureCreate).  Note: we don't need an explicit dependency
     684             :      * on aggTransType since we depend on it indirectly through transfn.
     685             :      * Likewise for aggmTransType using the mtransfunc, if it exists.
     686             :      */
     687             : 
     688             :     /* Depends on transition function */
     689          70 :     referenced.classId = ProcedureRelationId;
     690          70 :     referenced.objectId = transfn;
     691          70 :     referenced.objectSubId = 0;
     692          70 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     693             : 
     694             :     /* Depends on final function, if any */
     695          70 :     if (OidIsValid(finalfn))
     696             :     {
     697          27 :         referenced.classId = ProcedureRelationId;
     698          27 :         referenced.objectId = finalfn;
     699          27 :         referenced.objectSubId = 0;
     700          27 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     701             :     }
     702             : 
     703             :     /* Depends on combine function, if any */
     704          70 :     if (OidIsValid(combinefn))
     705             :     {
     706           1 :         referenced.classId = ProcedureRelationId;
     707           1 :         referenced.objectId = combinefn;
     708           1 :         referenced.objectSubId = 0;
     709           1 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     710             :     }
     711             : 
     712             :     /* Depends on serialization function, if any */
     713          70 :     if (OidIsValid(serialfn))
     714             :     {
     715           1 :         referenced.classId = ProcedureRelationId;
     716           1 :         referenced.objectId = serialfn;
     717           1 :         referenced.objectSubId = 0;
     718           1 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     719             :     }
     720             : 
     721             :     /* Depends on deserialization function, if any */
     722          70 :     if (OidIsValid(deserialfn))
     723             :     {
     724           1 :         referenced.classId = ProcedureRelationId;
     725           1 :         referenced.objectId = deserialfn;
     726           1 :         referenced.objectSubId = 0;
     727           1 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     728             :     }
     729             : 
     730             :     /* Depends on forward transition function, if any */
     731          70 :     if (OidIsValid(mtransfn))
     732             :     {
     733           6 :         referenced.classId = ProcedureRelationId;
     734           6 :         referenced.objectId = mtransfn;
     735           6 :         referenced.objectSubId = 0;
     736           6 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     737             :     }
     738             : 
     739             :     /* Depends on inverse transition function, if any */
     740          70 :     if (OidIsValid(minvtransfn))
     741             :     {
     742           6 :         referenced.classId = ProcedureRelationId;
     743           6 :         referenced.objectId = minvtransfn;
     744           6 :         referenced.objectSubId = 0;
     745           6 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     746             :     }
     747             : 
     748             :     /* Depends on final function, if any */
     749          70 :     if (OidIsValid(mfinalfn))
     750             :     {
     751           0 :         referenced.classId = ProcedureRelationId;
     752           0 :         referenced.objectId = mfinalfn;
     753           0 :         referenced.objectSubId = 0;
     754           0 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     755             :     }
     756             : 
     757             :     /* Depends on sort operator, if any */
     758          70 :     if (OidIsValid(sortop))
     759             :     {
     760           0 :         referenced.classId = OperatorRelationId;
     761           0 :         referenced.objectId = sortop;
     762           0 :         referenced.objectSubId = 0;
     763           0 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     764             :     }
     765             : 
     766          70 :     return myself;
     767             : }
     768             : 
     769             : /*
     770             :  * lookup_agg_function
     771             :  * common code for finding transfn, invtransfn, finalfn, and combinefn
     772             :  *
     773             :  * Returns OID of function, and stores its return type into *rettype
     774             :  *
     775             :  * NB: must not scribble on input_types[], as we may re-use those
     776             :  */
     777             : static Oid
     778         149 : lookup_agg_function(List *fnName,
     779             :                     int nargs,
     780             :                     Oid *input_types,
     781             :                     Oid variadicArgType,
     782             :                     Oid *rettype)
     783             : {
     784             :     Oid         fnOid;
     785             :     bool        retset;
     786             :     int         nvargs;
     787             :     Oid         vatype;
     788             :     Oid        *true_oid_array;
     789             :     FuncDetailCode fdresult;
     790             :     AclResult   aclresult;
     791             :     int         i;
     792             : 
     793             :     /*
     794             :      * func_get_detail looks up the function in the catalogs, does
     795             :      * disambiguation for polymorphic functions, handles inheritance, and
     796             :      * returns the funcid and type and set or singleton status of the
     797             :      * function's return value.  it also returns the true argument types to
     798             :      * the function.
     799             :      */
     800         149 :     fdresult = func_get_detail(fnName, NIL, NIL,
     801             :                                nargs, input_types, false, false,
     802             :                                &fnOid, rettype, &retset,
     803             :                                &nvargs, &vatype,
     804             :                                &true_oid_array, NULL);
     805             : 
     806             :     /* only valid case is a normal function not returning a set */
     807         149 :     if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
     808          24 :         ereport(ERROR,
     809             :                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
     810             :                  errmsg("function %s does not exist",
     811             :                         func_signature_string(fnName, nargs,
     812             :                                               NIL, input_types))));
     813         125 :     if (retset)
     814           0 :         ereport(ERROR,
     815             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
     816             :                  errmsg("function %s returns a set",
     817             :                         func_signature_string(fnName, nargs,
     818             :                                               NIL, input_types))));
     819             : 
     820             :     /*
     821             :      * If the agg is declared to take VARIADIC ANY, the underlying functions
     822             :      * had better be declared that way too, else they may receive too many
     823             :      * parameters; but func_get_detail would have been happy with plain ANY.
     824             :      * (Probably nothing very bad would happen, but it wouldn't work as the
     825             :      * user expects.)  Other combinations should work without any special
     826             :      * pushups, given that we told func_get_detail not to expand VARIADIC.
     827             :      */
     828         125 :     if (variadicArgType == ANYOID && vatype != ANYOID)
     829           0 :         ereport(ERROR,
     830             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
     831             :                  errmsg("function %s must accept VARIADIC ANY to be used in this aggregate",
     832             :                         func_signature_string(fnName, nargs,
     833             :                                               NIL, input_types))));
     834             : 
     835             :     /*
     836             :      * If there are any polymorphic types involved, enforce consistency, and
     837             :      * possibly refine the result type.  It's OK if the result is still
     838             :      * polymorphic at this point, though.
     839             :      */
     840         125 :     *rettype = enforce_generic_type_consistency(input_types,
     841             :                                                 true_oid_array,
     842             :                                                 nargs,
     843             :                                                 *rettype,
     844             :                                                 true);
     845             : 
     846             :     /*
     847             :      * func_get_detail will find functions requiring run-time argument type
     848             :      * coercion, but nodeAgg.c isn't prepared to deal with that
     849             :      */
     850         349 :     for (i = 0; i < nargs; i++)
     851             :     {
     852         224 :         if (!IsBinaryCoercible(input_types[i], true_oid_array[i]))
     853           0 :             ereport(ERROR,
     854             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
     855             :                      errmsg("function %s requires run-time type coercion",
     856             :                             func_signature_string(fnName, nargs,
     857             :                                                   NIL, true_oid_array))));
     858             :     }
     859             : 
     860             :     /* Check aggregate creator has permission to call the function */
     861         125 :     aclresult = pg_proc_aclcheck(fnOid, GetUserId(), ACL_EXECUTE);
     862         125 :     if (aclresult != ACLCHECK_OK)
     863           0 :         aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(fnOid));
     864             : 
     865         125 :     return fnOid;
     866             : }

Generated by: LCOV version 1.11