Header And Logo

PostgreSQL
| The world's most advanced open source database.

pg_aggregate.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * pg_aggregate.c
00004  *    routines to support manipulation of the pg_aggregate relation
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/catalog/pg_aggregate.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 #include "postgres.h"
00016 
00017 #include "access/heapam.h"
00018 #include "access/htup_details.h"
00019 #include "catalog/dependency.h"
00020 #include "catalog/indexing.h"
00021 #include "catalog/pg_aggregate.h"
00022 #include "catalog/pg_language.h"
00023 #include "catalog/pg_operator.h"
00024 #include "catalog/pg_proc.h"
00025 #include "catalog/pg_proc_fn.h"
00026 #include "catalog/pg_type.h"
00027 #include "miscadmin.h"
00028 #include "parser/parse_coerce.h"
00029 #include "parser/parse_func.h"
00030 #include "parser/parse_oper.h"
00031 #include "utils/acl.h"
00032 #include "utils/builtins.h"
00033 #include "utils/lsyscache.h"
00034 #include "utils/rel.h"
00035 #include "utils/syscache.h"
00036 
00037 
00038 static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types,
00039                     Oid *rettype);
00040 
00041 
00042 /*
00043  * AggregateCreate
00044  */
00045 Oid
00046 AggregateCreate(const char *aggName,
00047                 Oid aggNamespace,
00048                 Oid *aggArgTypes,
00049                 int numArgs,
00050                 List *aggtransfnName,
00051                 List *aggfinalfnName,
00052                 List *aggsortopName,
00053                 Oid aggTransType,
00054                 const char *agginitval)
00055 {
00056     Relation    aggdesc;
00057     HeapTuple   tup;
00058     bool        nulls[Natts_pg_aggregate];
00059     Datum       values[Natts_pg_aggregate];
00060     Form_pg_proc proc;
00061     Oid         transfn;
00062     Oid         finalfn = InvalidOid;   /* can be omitted */
00063     Oid         sortop = InvalidOid;    /* can be omitted */
00064     bool        hasPolyArg;
00065     bool        hasInternalArg;
00066     Oid         rettype;
00067     Oid         finaltype;
00068     Oid        *fnArgs;
00069     int         nargs_transfn;
00070     Oid         procOid;
00071     TupleDesc   tupDesc;
00072     int         i;
00073     ObjectAddress myself,
00074                 referenced;
00075     AclResult   aclresult;
00076 
00077     /* sanity checks (caller should have caught these) */
00078     if (!aggName)
00079         elog(ERROR, "no aggregate name supplied");
00080 
00081     if (!aggtransfnName)
00082         elog(ERROR, "aggregate must have a transition function");
00083 
00084     /* check for polymorphic and INTERNAL arguments */
00085     hasPolyArg = false;
00086     hasInternalArg = false;
00087     for (i = 0; i < numArgs; i++)
00088     {
00089         if (IsPolymorphicType(aggArgTypes[i]))
00090             hasPolyArg = true;
00091         else if (aggArgTypes[i] == INTERNALOID)
00092             hasInternalArg = true;
00093     }
00094 
00095     /*
00096      * If transtype is polymorphic, must have polymorphic argument also; else
00097      * we will have no way to deduce the actual transtype.
00098      */
00099     if (IsPolymorphicType(aggTransType) && !hasPolyArg)
00100         ereport(ERROR,
00101                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
00102                  errmsg("cannot determine transition data type"),
00103                  errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument.")));
00104 
00105     /* find the transfn */
00106     nargs_transfn = numArgs + 1;
00107     fnArgs = (Oid *) palloc(nargs_transfn * sizeof(Oid));
00108     fnArgs[0] = aggTransType;
00109     memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
00110     transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs,
00111                                   &rettype);
00112 
00113     /*
00114      * Return type of transfn (possibly after refinement by
00115      * enforce_generic_type_consistency, if transtype isn't polymorphic) must
00116      * exactly match declared transtype.
00117      *
00118      * In the non-polymorphic-transtype case, it might be okay to allow a
00119      * rettype that's binary-coercible to transtype, but I'm not quite
00120      * convinced that it's either safe or useful.  When transtype is
00121      * polymorphic we *must* demand exact equality.
00122      */
00123     if (rettype != aggTransType)
00124         ereport(ERROR,
00125                 (errcode(ERRCODE_DATATYPE_MISMATCH),
00126                  errmsg("return type of transition function %s is not %s",
00127                         NameListToString(aggtransfnName),
00128                         format_type_be(aggTransType))));
00129 
00130     tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(transfn));
00131     if (!HeapTupleIsValid(tup))
00132         elog(ERROR, "cache lookup failed for function %u", transfn);
00133     proc = (Form_pg_proc) GETSTRUCT(tup);
00134 
00135     /*
00136      * If the transfn is strict and the initval is NULL, make sure first input
00137      * type and transtype are the same (or at least binary-compatible), so
00138      * that it's OK to use the first input value as the initial transValue.
00139      */
00140     if (proc->proisstrict && agginitval == NULL)
00141     {
00142         if (numArgs < 1 ||
00143             !IsBinaryCoercible(aggArgTypes[0], aggTransType))
00144             ereport(ERROR,
00145                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
00146                      errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
00147     }
00148     ReleaseSysCache(tup);
00149 
00150     /* handle finalfn, if supplied */
00151     if (aggfinalfnName)
00152     {
00153         fnArgs[0] = aggTransType;
00154         finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs,
00155                                       &finaltype);
00156     }
00157     else
00158     {
00159         /*
00160          * If no finalfn, aggregate result type is type of the state value
00161          */
00162         finaltype = aggTransType;
00163     }
00164     Assert(OidIsValid(finaltype));
00165 
00166     /*
00167      * If finaltype (i.e. aggregate return type) is polymorphic, inputs must
00168      * be polymorphic also, else parser will fail to deduce result type.
00169      * (Note: given the previous test on transtype and inputs, this cannot
00170      * happen, unless someone has snuck a finalfn definition into the catalogs
00171      * that itself violates the rule against polymorphic result with no
00172      * polymorphic input.)
00173      */
00174     if (IsPolymorphicType(finaltype) && !hasPolyArg)
00175         ereport(ERROR,
00176                 (errcode(ERRCODE_DATATYPE_MISMATCH),
00177                  errmsg("cannot determine result data type"),
00178                  errdetail("An aggregate returning a polymorphic type "
00179                            "must have at least one polymorphic argument.")));
00180 
00181     /*
00182      * Also, the return type can't be INTERNAL unless there's at least one
00183      * INTERNAL argument.  This is the same type-safety restriction we enforce
00184      * for regular functions, but at the level of aggregates.  We must test
00185      * this explicitly because we allow INTERNAL as the transtype.
00186      */
00187     if (finaltype == INTERNALOID && !hasInternalArg)
00188         ereport(ERROR,
00189                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
00190                  errmsg("unsafe use of pseudo-type \"internal\""),
00191                  errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
00192 
00193     /* handle sortop, if supplied */
00194     if (aggsortopName)
00195     {
00196         if (numArgs != 1)
00197             ereport(ERROR,
00198                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
00199                      errmsg("sort operator can only be specified for single-argument aggregates")));
00200         sortop = LookupOperName(NULL, aggsortopName,
00201                                 aggArgTypes[0], aggArgTypes[0],
00202                                 false, -1);
00203     }
00204 
00205     /*
00206      * permission checks on used types
00207      */
00208     for (i = 0; i < numArgs; i++)
00209     {
00210         aclresult = pg_type_aclcheck(aggArgTypes[i], GetUserId(), ACL_USAGE);
00211         if (aclresult != ACLCHECK_OK)
00212             aclcheck_error_type(aclresult, aggArgTypes[i]);
00213     }
00214 
00215     aclresult = pg_type_aclcheck(aggTransType, GetUserId(), ACL_USAGE);
00216     if (aclresult != ACLCHECK_OK)
00217         aclcheck_error_type(aclresult, aggTransType);
00218 
00219     aclresult = pg_type_aclcheck(finaltype, GetUserId(), ACL_USAGE);
00220     if (aclresult != ACLCHECK_OK)
00221         aclcheck_error_type(aclresult, finaltype);
00222 
00223 
00224     /*
00225      * Everything looks okay.  Try to create the pg_proc entry for the
00226      * aggregate.  (This could fail if there's already a conflicting entry.)
00227      */
00228 
00229     procOid = ProcedureCreate(aggName,
00230                               aggNamespace,
00231                               false,    /* no replacement */
00232                               false,    /* doesn't return a set */
00233                               finaltype,        /* returnType */
00234                               GetUserId(),      /* proowner */
00235                               INTERNALlanguageId,       /* languageObjectId */
00236                               InvalidOid,       /* no validator */
00237                               "aggregate_dummy",        /* placeholder proc */
00238                               NULL,     /* probin */
00239                               true,     /* isAgg */
00240                               false,    /* isWindowFunc */
00241                               false,    /* security invoker (currently not
00242                                          * definable for agg) */
00243                               false,    /* isLeakProof */
00244                               false,    /* isStrict (not needed for agg) */
00245                               PROVOLATILE_IMMUTABLE,    /* volatility (not
00246                                                          * needed for agg) */
00247                               buildoidvector(aggArgTypes,
00248                                              numArgs),  /* paramTypes */
00249                               PointerGetDatum(NULL),    /* allParamTypes */
00250                               PointerGetDatum(NULL),    /* parameterModes */
00251                               PointerGetDatum(NULL),    /* parameterNames */
00252                               NIL,      /* parameterDefaults */
00253                               PointerGetDatum(NULL),    /* proconfig */
00254                               1,    /* procost */
00255                               0);       /* prorows */
00256 
00257     /*
00258      * Okay to create the pg_aggregate entry.
00259      */
00260 
00261     /* initialize nulls and values */
00262     for (i = 0; i < Natts_pg_aggregate; i++)
00263     {
00264         nulls[i] = false;
00265         values[i] = (Datum) NULL;
00266     }
00267     values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid);
00268     values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
00269     values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
00270     values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
00271     values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
00272     if (agginitval)
00273         values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval);
00274     else
00275         nulls[Anum_pg_aggregate_agginitval - 1] = true;
00276 
00277     aggdesc = heap_open(AggregateRelationId, RowExclusiveLock);
00278     tupDesc = aggdesc->rd_att;
00279 
00280     tup = heap_form_tuple(tupDesc, values, nulls);
00281     simple_heap_insert(aggdesc, tup);
00282 
00283     CatalogUpdateIndexes(aggdesc, tup);
00284 
00285     heap_close(aggdesc, RowExclusiveLock);
00286 
00287     /*
00288      * Create dependencies for the aggregate (above and beyond those already
00289      * made by ProcedureCreate).  Note: we don't need an explicit dependency
00290      * on aggTransType since we depend on it indirectly through transfn.
00291      */
00292     myself.classId = ProcedureRelationId;
00293     myself.objectId = procOid;
00294     myself.objectSubId = 0;
00295 
00296     /* Depends on transition function */
00297     referenced.classId = ProcedureRelationId;
00298     referenced.objectId = transfn;
00299     referenced.objectSubId = 0;
00300     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00301 
00302     /* Depends on final function, if any */
00303     if (OidIsValid(finalfn))
00304     {
00305         referenced.classId = ProcedureRelationId;
00306         referenced.objectId = finalfn;
00307         referenced.objectSubId = 0;
00308         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00309     }
00310 
00311     /* Depends on sort operator, if any */
00312     if (OidIsValid(sortop))
00313     {
00314         referenced.classId = OperatorRelationId;
00315         referenced.objectId = sortop;
00316         referenced.objectSubId = 0;
00317         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00318     }
00319 
00320     return procOid;
00321 }
00322 
00323 /*
00324  * lookup_agg_function -- common code for finding both transfn and finalfn
00325  */
00326 static Oid
00327 lookup_agg_function(List *fnName,
00328                     int nargs,
00329                     Oid *input_types,
00330                     Oid *rettype)
00331 {
00332     Oid         fnOid;
00333     bool        retset;
00334     int         nvargs;
00335     Oid        *true_oid_array;
00336     FuncDetailCode fdresult;
00337     AclResult   aclresult;
00338     int         i;
00339 
00340     /*
00341      * func_get_detail looks up the function in the catalogs, does
00342      * disambiguation for polymorphic functions, handles inheritance, and
00343      * returns the funcid and type and set or singleton status of the
00344      * function's return value.  it also returns the true argument types to
00345      * the function.
00346      */
00347     fdresult = func_get_detail(fnName, NIL, NIL,
00348                                nargs, input_types, false, false,
00349                                &fnOid, rettype, &retset, &nvargs,
00350                                &true_oid_array, NULL);
00351 
00352     /* only valid case is a normal function not returning a set */
00353     if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
00354         ereport(ERROR,
00355                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
00356                  errmsg("function %s does not exist",
00357                         func_signature_string(fnName, nargs,
00358                                               NIL, input_types))));
00359     if (retset)
00360         ereport(ERROR,
00361                 (errcode(ERRCODE_DATATYPE_MISMATCH),
00362                  errmsg("function %s returns a set",
00363                         func_signature_string(fnName, nargs,
00364                                               NIL, input_types))));
00365 
00366     /*
00367      * If there are any polymorphic types involved, enforce consistency, and
00368      * possibly refine the result type.  It's OK if the result is still
00369      * polymorphic at this point, though.
00370      */
00371     *rettype = enforce_generic_type_consistency(input_types,
00372                                                 true_oid_array,
00373                                                 nargs,
00374                                                 *rettype,
00375                                                 true);
00376 
00377     /*
00378      * func_get_detail will find functions requiring run-time argument type
00379      * coercion, but nodeAgg.c isn't prepared to deal with that
00380      */
00381     for (i = 0; i < nargs; i++)
00382     {
00383         if (!IsPolymorphicType(true_oid_array[i]) &&
00384             !IsBinaryCoercible(input_types[i], true_oid_array[i]))
00385             ereport(ERROR,
00386                     (errcode(ERRCODE_DATATYPE_MISMATCH),
00387                      errmsg("function %s requires run-time type coercion",
00388                             func_signature_string(fnName, nargs,
00389                                                   NIL, true_oid_array))));
00390     }
00391 
00392     /* Check aggregate creator has permission to call the function */
00393     aclresult = pg_proc_aclcheck(fnOid, GetUserId(), ACL_EXECUTE);
00394     if (aclresult != ACLCHECK_OK)
00395         aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(fnOid));
00396 
00397     return fnOid;
00398 }