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

Generated by: LCOV version 2.0-1