/* * LookupOperWithArgs * Like LookupOperName, but the argument types are specified by * a ObjectWithArg node. */ Oid LookupOperWithArgs(ObjectWithArgs *oper, bool noError) { TypeName *oprleft, *oprright; Oid leftoid, rightoid; Assert(list_length(oper->objargs) == 2); oprleft = linitial(oper->objargs); oprright = lsecond(oper->objargs); if (oprleft == NULL) leftoid = InvalidOid; else leftoid = LookupTypeNameOid(NULL, oprleft, noError); if (oprright == NULL) rightoid = InvalidOid; else rightoid = LookupTypeNameOid(NULL, oprright, noError); return LookupOperName(NULL, oper->objname, leftoid, rightoid, noError, -1); }
/* * LookupOperNameTypeNames * Like LookupOperName, but the argument types are specified by * TypeName nodes. * * Pass oprleft = NULL for a prefix op, oprright = NULL for a postfix op. */ Oid LookupOperNameTypeNames(ParseState *pstate, List *opername, TypeName *oprleft, TypeName *oprright, bool noError, int location) { Oid leftoid, rightoid; if (oprleft == NULL) leftoid = InvalidOid; else leftoid = typenameTypeId(pstate, oprleft); if (oprright == NULL) rightoid = InvalidOid; else rightoid = typenameTypeId(pstate, oprright); return LookupOperName(pstate, opername, leftoid, rightoid, noError, location); }
/* * OperatorLookup * * looks up an operator given a possibly-qualified name and * left and right type IDs. * * *defined is set TRUE if defined (not a shell) */ static Oid OperatorLookup(List *operatorName, Oid leftObjectId, Oid rightObjectId, bool *defined) { Oid operatorObjectId; RegProcedure oprcode; operatorObjectId = LookupOperName(operatorName, leftObjectId, rightObjectId, true); if (!OidIsValid(operatorObjectId)) { *defined = false; return InvalidOid; } oprcode = get_opcode(operatorObjectId); *defined = RegProcedureIsValid(oprcode); return operatorObjectId; }
/* * AggregateCreate */ ObjectAddress AggregateCreate(const char *aggName, Oid aggNamespace, char aggKind, int numArgs, int numDirectArgs, oidvector *parameterTypes, Datum allParameterTypes, Datum parameterModes, Datum parameterNames, List *parameterDefaults, Oid variadicArgType, List *aggtransfnName, List *aggfinalfnName, List *aggcombinefnName, List *aggserialfnName, List *aggdeserialfnName, List *aggmtransfnName, List *aggminvtransfnName, List *aggmfinalfnName, bool finalfnExtraArgs, bool mfinalfnExtraArgs, List *aggsortopName, Oid aggTransType, Oid aggSerialType, int32 aggTransSpace, Oid aggmTransType, int32 aggmTransSpace, const char *agginitval, const char *aggminitval, char proparallel) { Relation aggdesc; HeapTuple tup; bool nulls[Natts_pg_aggregate]; Datum values[Natts_pg_aggregate]; Form_pg_proc proc; Oid transfn; Oid finalfn = InvalidOid; /* can be omitted */ Oid combinefn = InvalidOid; /* can be omitted */ Oid serialfn = InvalidOid; /* can be omitted */ Oid deserialfn = InvalidOid; /* can be omitted */ Oid mtransfn = InvalidOid; /* can be omitted */ Oid minvtransfn = InvalidOid; /* can be omitted */ Oid mfinalfn = InvalidOid; /* can be omitted */ Oid sortop = InvalidOid; /* can be omitted */ Oid *aggArgTypes = parameterTypes->values; bool hasPolyArg; bool hasInternalArg; bool mtransIsStrict = false; Oid rettype; Oid finaltype; Oid fnArgs[FUNC_MAX_ARGS]; int nargs_transfn; int nargs_finalfn; Oid procOid; TupleDesc tupDesc; int i; ObjectAddress myself, referenced; AclResult aclresult; /* sanity checks (caller should have caught these) */ if (!aggName) elog(ERROR, "no aggregate name supplied"); if (!aggtransfnName) elog(ERROR, "aggregate must have a transition function"); if (numDirectArgs < 0 || numDirectArgs > numArgs) elog(ERROR, "incorrect number of direct args for aggregate"); /* * Aggregates can have at most FUNC_MAX_ARGS-1 args, else the transfn * and/or finalfn will be unrepresentable in pg_proc. We must check now * to protect fixed-size arrays here and possibly in called functions. */ if (numArgs < 0 || numArgs > FUNC_MAX_ARGS - 1) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg_plural("aggregates cannot have more than %d argument", "aggregates cannot have more than %d arguments", FUNC_MAX_ARGS - 1, FUNC_MAX_ARGS - 1))); /* check for polymorphic and INTERNAL arguments */ hasPolyArg = false; hasInternalArg = false; for (i = 0; i < numArgs; i++) { if (IsPolymorphicType(aggArgTypes[i])) hasPolyArg = true; else if (aggArgTypes[i] == INTERNALOID) hasInternalArg = true; } /* * If transtype is polymorphic, must have polymorphic argument also; else * we will have no way to deduce the actual transtype. */ if (IsPolymorphicType(aggTransType) && !hasPolyArg) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine transition data type"), errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument."))); /* * Likewise for moving-aggregate transtype, if any */ if (OidIsValid(aggmTransType) && IsPolymorphicType(aggmTransType) && !hasPolyArg) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine transition data type"), errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument."))); /* * An ordered-set aggregate that is VARIADIC must be VARIADIC ANY. In * principle we could support regular variadic types, but it would make * things much more complicated because we'd have to assemble the correct * subsets of arguments into array values. Since no standard aggregates * have use for such a case, we aren't bothering for now. */ if (AGGKIND_IS_ORDERED_SET(aggKind) && OidIsValid(variadicArgType) && variadicArgType != ANYOID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("a variadic ordered-set aggregate must use VARIADIC type ANY"))); /* * If it's a hypothetical-set aggregate, there must be at least as many * direct arguments as aggregated ones, and the last N direct arguments * must match the aggregated ones in type. (We have to check this again * when the aggregate is called, in case ANY is involved, but it makes * sense to reject the aggregate definition now if the declared arg types * don't match up.) It's unconditionally OK if numDirectArgs == numArgs, * indicating that the grammar merged identical VARIADIC entries from both * lists. Otherwise, if the agg is VARIADIC, then we had VARIADIC only on * the aggregated side, which is not OK. Otherwise, insist on the last N * parameter types on each side matching exactly. */ if (aggKind == AGGKIND_HYPOTHETICAL && numDirectArgs < numArgs) { int numAggregatedArgs = numArgs - numDirectArgs; if (OidIsValid(variadicArgType) || numDirectArgs < numAggregatedArgs || memcmp(aggArgTypes + (numDirectArgs - numAggregatedArgs), aggArgTypes + numDirectArgs, numAggregatedArgs * sizeof(Oid)) != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("a hypothetical-set aggregate must have direct arguments matching its aggregated arguments"))); } /* * Find the transfn. For ordinary aggs, it takes the transtype plus all * aggregate arguments. For ordered-set aggs, it takes the transtype plus * all aggregated args, but not direct args. However, we have to treat * specially the case where a trailing VARIADIC item is considered to * cover both direct and aggregated args. */ if (AGGKIND_IS_ORDERED_SET(aggKind)) { if (numDirectArgs < numArgs) nargs_transfn = numArgs - numDirectArgs + 1; else { /* special case with VARIADIC last arg */ Assert(variadicArgType != InvalidOid); nargs_transfn = 2; } fnArgs[0] = aggTransType; memcpy(fnArgs + 1, aggArgTypes + (numArgs - (nargs_transfn - 1)), (nargs_transfn - 1) * sizeof(Oid)); } else { nargs_transfn = numArgs + 1; fnArgs[0] = aggTransType; memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid)); } transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs, variadicArgType, &rettype); /* * Return type of transfn (possibly after refinement by * enforce_generic_type_consistency, if transtype isn't polymorphic) must * exactly match declared transtype. * * In the non-polymorphic-transtype case, it might be okay to allow a * rettype that's binary-coercible to transtype, but I'm not quite * convinced that it's either safe or useful. When transtype is * polymorphic we *must* demand exact equality. */ if (rettype != aggTransType) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of transition function %s is not %s", NameListToString(aggtransfnName), format_type_be(aggTransType)))); tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(transfn)); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for function %u", transfn); proc = (Form_pg_proc) GETSTRUCT(tup); /* * If the transfn is strict and the initval is NULL, make sure first input * type and transtype are the same (or at least binary-compatible), so * that it's OK to use the first input value as the initial transValue. */ if (proc->proisstrict && agginitval == NULL) { if (numArgs < 1 || !IsBinaryCoercible(aggArgTypes[0], aggTransType)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type"))); } ReleaseSysCache(tup); /* handle moving-aggregate transfn, if supplied */ if (aggmtransfnName) { /* * The arguments are the same as for the regular transfn, except that * the transition data type might be different. So re-use the fnArgs * values set up above, except for that one. */ Assert(OidIsValid(aggmTransType)); fnArgs[0] = aggmTransType; mtransfn = lookup_agg_function(aggmtransfnName, nargs_transfn, fnArgs, variadicArgType, &rettype); /* As above, return type must exactly match declared mtranstype. */ if (rettype != aggmTransType) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of transition function %s is not %s", NameListToString(aggmtransfnName), format_type_be(aggmTransType)))); tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(mtransfn)); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for function %u", mtransfn); proc = (Form_pg_proc) GETSTRUCT(tup); /* * If the mtransfn is strict and the minitval is NULL, check first * input type and mtranstype are binary-compatible. */ if (proc->proisstrict && aggminitval == NULL) { if (numArgs < 1 || !IsBinaryCoercible(aggArgTypes[0], aggmTransType)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type"))); } /* Remember if mtransfn is strict; we may need this below */ mtransIsStrict = proc->proisstrict; ReleaseSysCache(tup); } /* handle minvtransfn, if supplied */ if (aggminvtransfnName) { /* * This must have the same number of arguments with the same types as * the forward transition function, so just re-use the fnArgs data. */ Assert(aggmtransfnName); minvtransfn = lookup_agg_function(aggminvtransfnName, nargs_transfn, fnArgs, variadicArgType, &rettype); /* As above, return type must exactly match declared mtranstype. */ if (rettype != aggmTransType) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of inverse transition function %s is not %s", NameListToString(aggminvtransfnName), format_type_be(aggmTransType)))); tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(minvtransfn)); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for function %u", minvtransfn); proc = (Form_pg_proc) GETSTRUCT(tup); /* * We require the strictness settings of the forward and inverse * transition functions to agree. This saves having to handle * assorted special cases at execution time. */ if (proc->proisstrict != mtransIsStrict) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("strictness of aggregate's forward and inverse transition functions must match"))); ReleaseSysCache(tup); } /* handle finalfn, if supplied */ if (aggfinalfnName) { /* * If finalfnExtraArgs is specified, the transfn takes the transtype * plus all args; otherwise, it just takes the transtype plus any * direct args. (Non-direct args are useless at runtime, and are * actually passed as NULLs, but we may need them in the function * signature to allow resolution of a polymorphic agg's result type.) */ Oid ffnVariadicArgType = variadicArgType; fnArgs[0] = aggTransType; memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid)); if (finalfnExtraArgs) nargs_finalfn = numArgs + 1; else { nargs_finalfn = numDirectArgs + 1; if (numDirectArgs < numArgs) { /* variadic argument doesn't affect finalfn */ ffnVariadicArgType = InvalidOid; } } finalfn = lookup_agg_function(aggfinalfnName, nargs_finalfn, fnArgs, ffnVariadicArgType, &finaltype); /* * When finalfnExtraArgs is specified, the finalfn will certainly be * passed at least one null argument, so complain if it's strict. * Nothing bad would happen at runtime (you'd just get a null result), * but it's surely not what the user wants, so let's complain now. */ if (finalfnExtraArgs && func_strict(finalfn)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("final function with extra arguments must not be declared STRICT"))); } else { /* * If no finalfn, aggregate result type is type of the state value */ finaltype = aggTransType; } Assert(OidIsValid(finaltype)); /* handle the combinefn, if supplied */ if (aggcombinefnName) { Oid combineType; /* * Combine function must have 2 argument, each of which is the trans * type */ fnArgs[0] = aggTransType; fnArgs[1] = aggTransType; combinefn = lookup_agg_function(aggcombinefnName, 2, fnArgs, variadicArgType, &combineType); /* Ensure the return type matches the aggregates trans type */ if (combineType != aggTransType) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of combine function %s is not %s", NameListToString(aggcombinefnName), format_type_be(aggTransType)))); /* * A combine function to combine INTERNAL states must accept nulls and * ensure that the returned state is in the correct memory context. */ if (aggTransType == INTERNALOID && func_strict(combinefn)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("combine function with \"%s\" transition type must not be declared STRICT", format_type_be(aggTransType)))); } /* * Validate the serialization function, if present. We must ensure that * the return type of this function is the same as the specified * serialType. */ if (aggserialfnName) { fnArgs[0] = aggTransType; serialfn = lookup_agg_function(aggserialfnName, 1, fnArgs, variadicArgType, &rettype); if (rettype != aggSerialType) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of serialization function %s is not %s", NameListToString(aggserialfnName), format_type_be(aggSerialType)))); } /* * Validate the deserialization function, if present. We must ensure that * the return type of this function is the same as the transType. */ if (aggdeserialfnName) { fnArgs[0] = aggSerialType; deserialfn = lookup_agg_function(aggdeserialfnName, 1, fnArgs, variadicArgType, &rettype); if (rettype != aggTransType) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of deserialization function %s is not %s", NameListToString(aggdeserialfnName), format_type_be(aggTransType)))); } /* * If finaltype (i.e. aggregate return type) is polymorphic, inputs must * be polymorphic also, else parser will fail to deduce result type. * (Note: given the previous test on transtype and inputs, this cannot * happen, unless someone has snuck a finalfn definition into the catalogs * that itself violates the rule against polymorphic result with no * polymorphic input.) */ if (IsPolymorphicType(finaltype) && !hasPolyArg) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot determine result data type"), errdetail("An aggregate returning a polymorphic type " "must have at least one polymorphic argument."))); /* * Also, the return type can't be INTERNAL unless there's at least one * INTERNAL argument. This is the same type-safety restriction we enforce * for regular functions, but at the level of aggregates. We must test * this explicitly because we allow INTERNAL as the transtype. */ if (finaltype == INTERNALOID && !hasInternalArg) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); /* * If a moving-aggregate implementation is supplied, look up its finalfn * if any, and check that the implied aggregate result type matches the * plain implementation. */ if (OidIsValid(aggmTransType)) { /* handle finalfn, if supplied */ if (aggmfinalfnName) { /* * The arguments are figured the same way as for the regular * finalfn, but using aggmTransType and mfinalfnExtraArgs. */ Oid ffnVariadicArgType = variadicArgType; fnArgs[0] = aggmTransType; memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid)); if (mfinalfnExtraArgs) nargs_finalfn = numArgs + 1; else { nargs_finalfn = numDirectArgs + 1; if (numDirectArgs < numArgs) { /* variadic argument doesn't affect finalfn */ ffnVariadicArgType = InvalidOid; } } mfinalfn = lookup_agg_function(aggmfinalfnName, nargs_finalfn, fnArgs, ffnVariadicArgType, &rettype); /* As above, check strictness if mfinalfnExtraArgs is given */ if (mfinalfnExtraArgs && func_strict(mfinalfn)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("final function with extra arguments must not be declared STRICT"))); } else { /* * If no finalfn, aggregate result type is type of the state value */ rettype = aggmTransType; } Assert(OidIsValid(rettype)); if (rettype != finaltype) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("moving-aggregate implementation returns type %s, but plain implementation returns type %s", format_type_be(aggmTransType), format_type_be(aggTransType)))); } /* handle sortop, if supplied */ if (aggsortopName) { if (numArgs != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("sort operator can only be specified for single-argument aggregates"))); sortop = LookupOperName(NULL, aggsortopName, aggArgTypes[0], aggArgTypes[0], false, -1); } /* * permission checks on used types */ for (i = 0; i < numArgs; i++) { aclresult = pg_type_aclcheck(aggArgTypes[i], GetUserId(), ACL_USAGE); if (aclresult != ACLCHECK_OK) aclcheck_error_type(aclresult, aggArgTypes[i]); } aclresult = pg_type_aclcheck(aggTransType, GetUserId(), ACL_USAGE); if (aclresult != ACLCHECK_OK) aclcheck_error_type(aclresult, aggTransType); if (OidIsValid(aggmTransType)) { aclresult = pg_type_aclcheck(aggmTransType, GetUserId(), ACL_USAGE); if (aclresult != ACLCHECK_OK) aclcheck_error_type(aclresult, aggmTransType); } aclresult = pg_type_aclcheck(finaltype, GetUserId(), ACL_USAGE); if (aclresult != ACLCHECK_OK) aclcheck_error_type(aclresult, finaltype); /* * Everything looks okay. Try to create the pg_proc entry for the * aggregate. (This could fail if there's already a conflicting entry.) */ myself = ProcedureCreate(aggName, aggNamespace, false, /* no replacement */ false, /* doesn't return a set */ finaltype, /* returnType */ GetUserId(), /* proowner */ INTERNALlanguageId, /* languageObjectId */ InvalidOid, /* no validator */ "aggregate_dummy", /* placeholder proc */ NULL, /* probin */ true, /* isAgg */ false, /* isWindowFunc */ false, /* security invoker (currently not * definable for agg) */ false, /* isLeakProof */ false, /* isStrict (not needed for agg) */ PROVOLATILE_IMMUTABLE, /* volatility (not * needed for agg) */ proparallel, parameterTypes, /* paramTypes */ allParameterTypes, /* allParamTypes */ parameterModes, /* parameterModes */ parameterNames, /* parameterNames */ parameterDefaults, /* parameterDefaults */ PointerGetDatum(NULL), /* trftypes */ PointerGetDatum(NULL), /* proconfig */ 1, /* procost */ 0); /* prorows */ procOid = myself.objectId; /* * Okay to create the pg_aggregate entry. */ /* initialize nulls and values */ for (i = 0; i < Natts_pg_aggregate; i++) { nulls[i] = false; values[i] = (Datum) NULL; } values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid); values[Anum_pg_aggregate_aggkind - 1] = CharGetDatum(aggKind); values[Anum_pg_aggregate_aggnumdirectargs - 1] = Int16GetDatum(numDirectArgs); values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn); values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn); values[Anum_pg_aggregate_aggcombinefn - 1] = ObjectIdGetDatum(combinefn); values[Anum_pg_aggregate_aggserialfn - 1] = ObjectIdGetDatum(serialfn); values[Anum_pg_aggregate_aggdeserialfn - 1] = ObjectIdGetDatum(deserialfn); values[Anum_pg_aggregate_aggmtransfn - 1] = ObjectIdGetDatum(mtransfn); values[Anum_pg_aggregate_aggminvtransfn - 1] = ObjectIdGetDatum(minvtransfn); values[Anum_pg_aggregate_aggmfinalfn - 1] = ObjectIdGetDatum(mfinalfn); values[Anum_pg_aggregate_aggfinalextra - 1] = BoolGetDatum(finalfnExtraArgs); values[Anum_pg_aggregate_aggmfinalextra - 1] = BoolGetDatum(mfinalfnExtraArgs); values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop); values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType); values[Anum_pg_aggregate_aggserialtype - 1] = ObjectIdGetDatum(aggSerialType); values[Anum_pg_aggregate_aggtransspace - 1] = Int32GetDatum(aggTransSpace); values[Anum_pg_aggregate_aggmtranstype - 1] = ObjectIdGetDatum(aggmTransType); values[Anum_pg_aggregate_aggmtransspace - 1] = Int32GetDatum(aggmTransSpace); if (agginitval) values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval); else nulls[Anum_pg_aggregate_agginitval - 1] = true; if (aggminitval) values[Anum_pg_aggregate_aggminitval - 1] = CStringGetTextDatum(aggminitval); else nulls[Anum_pg_aggregate_aggminitval - 1] = true; aggdesc = heap_open(AggregateRelationId, RowExclusiveLock); tupDesc = aggdesc->rd_att; tup = heap_form_tuple(tupDesc, values, nulls); simple_heap_insert(aggdesc, tup); CatalogUpdateIndexes(aggdesc, tup); heap_close(aggdesc, RowExclusiveLock); /* * Create dependencies for the aggregate (above and beyond those already * made by ProcedureCreate). Note: we don't need an explicit dependency * on aggTransType since we depend on it indirectly through transfn. * Likewise for aggmTransType using the mtransfunc, and also for * aggSerialType using the serialfn, if they exist. */ /* Depends on transition function */ referenced.classId = ProcedureRelationId; referenced.objectId = transfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* Depends on final function, if any */ if (OidIsValid(finalfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = finalfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on combine function, if any */ if (OidIsValid(combinefn)) { referenced.classId = ProcedureRelationId; referenced.objectId = combinefn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on serialization function, if any */ if (OidIsValid(serialfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = serialfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on deserialization function, if any */ if (OidIsValid(deserialfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = deserialfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on forward transition function, if any */ if (OidIsValid(mtransfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = mtransfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on inverse transition function, if any */ if (OidIsValid(minvtransfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = minvtransfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on final function, if any */ if (OidIsValid(mfinalfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = mfinalfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on sort operator, if any */ if (OidIsValid(sortop)) { referenced.classId = OperatorRelationId; referenced.objectId = sortop; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } return myself; }
/* * AggregateCreateWithOid */ Oid AggregateCreateWithOid(const char *aggName, Oid aggNamespace, Oid *aggArgTypes, int numArgs, List *aggtransfnName, List *aggprelimfnName, List *aggfinalfnName, List *aggsortopName, Oid aggTransType, const char *agginitval, bool aggordered, Oid procOid) { HeapTuple tup; bool nulls[Natts_pg_aggregate]; Datum values[Natts_pg_aggregate]; Form_pg_proc proc; Oid transfn; Oid invtransfn = InvalidOid; /* MPP windowing optimization */ Oid prelimfn = InvalidOid; /* if omitted, disables MPP 2-stage for this aggregate */ Oid invprelimfn = InvalidOid; /* MPP windowing optimization */ Oid finalfn = InvalidOid; /* can be omitted */ Oid sortop = InvalidOid; /* can be omitted */ bool hasPolyArg; bool hasInternalArg; Oid rettype; Oid finaltype; Oid prelimrettype; Oid *fnArgs; int nargs_transfn; int i; ObjectAddress myself, referenced; cqContext *pcqCtx; cqContext *pcqCtx2; /* sanity checks (caller should have caught these) */ if (!aggName) elog(ERROR, "no aggregate name supplied"); if (!aggtransfnName) elog(ERROR, "aggregate must have a transition function"); /* check for polymorphic arguments and INTERNAL arguments */ hasPolyArg = false; hasInternalArg = false; for (i = 0; i < numArgs; i++) { if (aggArgTypes[i] == ANYARRAYOID || aggArgTypes[i] == ANYELEMENTOID) hasPolyArg = true; else if (aggArgTypes[i] == INTERNALOID) hasInternalArg = true; } /* * If transtype is polymorphic, must have polymorphic argument also; else * we will have no way to deduce the actual transtype. */ if (!hasPolyArg && (aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine transition data type"), errdetail("An aggregate using \"anyarray\" or \"anyelement\" as transition type must have at least one argument of either type."))); /* find the transfn */ nargs_transfn = numArgs + 1; fnArgs = (Oid *) palloc(nargs_transfn * sizeof(Oid)); fnArgs[0] = aggTransType; memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid)); transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs, &rettype); elog(DEBUG5,"AggregateCreateWithOid: successfully located transition " "function %s with return type %d", func_signature_string(aggtransfnName, nargs_transfn, fnArgs), rettype); /* * Return type of transfn (possibly after refinement by * enforce_generic_type_consistency, if transtype isn't polymorphic) must * exactly match declared transtype. * * In the non-polymorphic-transtype case, it might be okay to allow a * rettype that's binary-coercible to transtype, but I'm not quite * convinced that it's either safe or useful. When transtype is * polymorphic we *must* demand exact equality. */ if (rettype != aggTransType) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of transition function %s is not %s", NameListToString(aggtransfnName), format_type_be(aggTransType)))); pcqCtx2 = caql_beginscan( NULL, cql("SELECT * FROM pg_proc " " WHERE oid = :1 ", ObjectIdGetDatum(transfn))); tup = caql_getnext(pcqCtx2); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for function %u", transfn); proc = (Form_pg_proc) GETSTRUCT(tup); /* * If the transfn is strict and the initval is NULL, make sure first input * type and transtype are the same (or at least binary-compatible), so * that it's OK to use the first input value as the initial transValue. */ if (proc->proisstrict && agginitval == NULL) { if (numArgs < 1 || !IsBinaryCoercible(aggArgTypes[0], aggTransType)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type"))); } caql_endscan(pcqCtx2); /* handle prelimfn, if supplied */ if (aggprelimfnName) { /* * The preliminary state function (pfunc) input arguments are the results of the * state transition function (sfunc) and therefore must be of the same types. */ fnArgs[0] = rettype; fnArgs[1] = rettype; /* * Check that such a function name and prototype exists in the catalog. */ prelimfn = lookup_agg_function(aggprelimfnName, 2, fnArgs, &prelimrettype); elog(DEBUG5,"AggregateCreateWithOid: successfully located preliminary " "function %s with return type %d", func_signature_string(aggprelimfnName, 2, fnArgs), prelimrettype); Assert(OidIsValid(prelimrettype)); /* * The preliminary return type must be of the same type as the internal * state. (See similar error checking for transition types above) */ if (prelimrettype != rettype) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of preliminary function %s is not %s", NameListToString(aggprelimfnName), format_type_be(rettype)))); } /* handle finalfn, if supplied */ if (aggfinalfnName) { fnArgs[0] = aggTransType; finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs, &finaltype); } else { /* * If no finalfn, aggregate result type is type of the state value */ finaltype = aggTransType; } Assert(OidIsValid(finaltype)); /* * If finaltype (i.e. aggregate return type) is polymorphic, inputs must * be polymorphic also, else parser will fail to deduce result type. * (Note: given the previous test on transtype and inputs, this cannot * happen, unless someone has snuck a finalfn definition into the catalogs * that itself violates the rule against polymorphic result with no * polymorphic input.) */ if (!hasPolyArg && (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot determine result data type"), errdetail("An aggregate returning \"anyarray\" or \"anyelement\" " "must have at least one argument of either type."))); /* * Also, the return type can't be INTERNAL unless there's at least one * INTERNAL argument. This is the same type-safety restriction we * enforce for regular functions, but at the level of aggregates. We * must test this explicitly because we allow INTERNAL as the transtype. */ if (finaltype == INTERNALOID && !hasInternalArg) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); /* handle sortop, if supplied */ if (aggsortopName) { if (numArgs != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("sort operator can only be specified for single-argument aggregates"))); sortop = LookupOperName(NULL, aggsortopName, aggArgTypes[0], aggArgTypes[0], false, -1); } /* * Everything looks okay. Try to create the pg_proc entry for the * aggregate. (This could fail if there's already a conflicting entry.) */ procOid = ProcedureCreate(aggName, aggNamespace, false, /* no replacement */ false, /* doesn't return a set */ finaltype, /* returnType */ INTERNALlanguageId, /* languageObjectId */ InvalidOid, /* no validator */ InvalidOid, /* no describe function */ "aggregate_dummy", /* placeholder proc */ NULL, /* probin */ true, /* isAgg */ false, /* isWin */ false, /* security invoker (currently not * definable for agg) */ false, /* isStrict (not needed for agg) */ PROVOLATILE_IMMUTABLE, /* volatility (not * needed for agg) */ buildoidvector(aggArgTypes, numArgs), /* paramTypes */ PointerGetDatum(NULL), /* allParamTypes */ PointerGetDatum(NULL), /* parameterModes */ PointerGetDatum(NULL), /* parameterNames */ NIL, /* parameterDefaults */ 1, /* procost */ 0, /* prorows */ PRODATAACCESS_NONE, /* prodataaccess */ procOid); /* * Okay to create the pg_aggregate entry. */ /* initialize nulls and values */ for (i = 0; i < Natts_pg_aggregate; i++) { nulls[i] = false; values[i] = (Datum) 0; } values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid); values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn); values[Anum_pg_aggregate_agginvtransfn - 1] = ObjectIdGetDatum(invtransfn); values[Anum_pg_aggregate_aggprelimfn - 1] = ObjectIdGetDatum(prelimfn); values[Anum_pg_aggregate_agginvprelimfn - 1] = ObjectIdGetDatum(invprelimfn); values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn); values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop); values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType); if (agginitval) values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval); else nulls[Anum_pg_aggregate_agginitval - 1] = true; values[Anum_pg_aggregate_aggordered - 1] = BoolGetDatum(aggordered); pcqCtx = caql_beginscan( NULL, cql("INSERT INTO pg_aggregate", NULL)); tup = caql_form_tuple(pcqCtx, values, nulls); /* insert a new tuple */ caql_insert(pcqCtx, tup); /* implicit update of index as well */ caql_endscan(pcqCtx); /* * Create dependencies for the aggregate (above and beyond those already * made by ProcedureCreate). Note: we don't need an explicit dependency * on aggTransType since we depend on it indirectly through transfn. */ myself.classId = ProcedureRelationId; myself.objectId = procOid; myself.objectSubId = 0; /* Depends on transition function */ referenced.classId = ProcedureRelationId; referenced.objectId = transfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* Depends on inverse transition function, if any */ if (OidIsValid(invtransfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = invtransfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on preliminary aggregation function, if any */ if (OidIsValid(prelimfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = prelimfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on inverse preliminary aggregation function, if any */ if (OidIsValid(invprelimfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = invprelimfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on final function, if any */ if (OidIsValid(finalfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = finalfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on sort operator, if any */ if (OidIsValid(sortop)) { referenced.classId = OperatorRelationId; referenced.objectId = sortop; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } return procOid; }
/* * AggregateCreate */ void AggregateCreate(const char *aggName, Oid aggNamespace, Oid *aggArgTypes, int numArgs, List *aggtransfnName, List *aggfinalfnName, List *aggsortopName, Oid aggTransType, const char *agginitval) { Relation aggdesc; HeapTuple tup; bool nulls[Natts_pg_aggregate]; Datum values[Natts_pg_aggregate]; Form_pg_proc proc; Oid transfn; Oid finalfn = InvalidOid; /* can be omitted */ Oid sortop = InvalidOid; /* can be omitted */ bool hasPolyArg; bool hasInternalArg; Oid rettype; Oid finaltype; Oid *fnArgs; int nargs_transfn; Oid procOid; TupleDesc tupDesc; int i; ObjectAddress myself, referenced; /* sanity checks (caller should have caught these) */ if (!aggName) elog(ERROR, "no aggregate name supplied"); if (!aggtransfnName) elog(ERROR, "aggregate must have a transition function"); /* check for polymorphic and INTERNAL arguments */ hasPolyArg = false; hasInternalArg = false; for (i = 0; i < numArgs; i++) { if (IsPolymorphicType(aggArgTypes[i])) hasPolyArg = true; else if (aggArgTypes[i] == INTERNALOID) hasInternalArg = true; } /* * If transtype is polymorphic, must have polymorphic argument also; else * we will have no way to deduce the actual transtype. */ if (IsPolymorphicType(aggTransType) && !hasPolyArg) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine transition data type"), errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument."))); /* find the transfn */ nargs_transfn = numArgs + 1; fnArgs = (Oid *) palloc(nargs_transfn * sizeof(Oid)); fnArgs[0] = aggTransType; memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid)); transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs, &rettype); /* * Return type of transfn (possibly after refinement by * enforce_generic_type_consistency, if transtype isn't polymorphic) must * exactly match declared transtype. * * In the non-polymorphic-transtype case, it might be okay to allow a * rettype that's binary-coercible to transtype, but I'm not quite * convinced that it's either safe or useful. When transtype is * polymorphic we *must* demand exact equality. */ if (rettype != aggTransType) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of transition function %s is not %s", NameListToString(aggtransfnName), format_type_be(aggTransType)))); tup = SearchSysCache(PROCOID, ObjectIdGetDatum(transfn), 0, 0, 0); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for function %u", transfn); proc = (Form_pg_proc) GETSTRUCT(tup); /* * If the transfn is strict and the initval is NULL, make sure first input * type and transtype are the same (or at least binary-compatible), so * that it's OK to use the first input value as the initial transValue. */ if (proc->proisstrict && agginitval == NULL) { if (numArgs < 1 || !IsBinaryCoercible(aggArgTypes[0], aggTransType)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type"))); } ReleaseSysCache(tup); /* handle finalfn, if supplied */ if (aggfinalfnName) { fnArgs[0] = aggTransType; finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs, &finaltype); } else { /* * If no finalfn, aggregate result type is type of the state value */ finaltype = aggTransType; } Assert(OidIsValid(finaltype)); /* * If finaltype (i.e. aggregate return type) is polymorphic, inputs must * be polymorphic also, else parser will fail to deduce result type. * (Note: given the previous test on transtype and inputs, this cannot * happen, unless someone has snuck a finalfn definition into the catalogs * that itself violates the rule against polymorphic result with no * polymorphic input.) */ if (IsPolymorphicType(finaltype) && !hasPolyArg) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot determine result data type"), errdetail("An aggregate returning a polymorphic type " "must have at least one polymorphic argument."))); /* * Also, the return type can't be INTERNAL unless there's at least one * INTERNAL argument. This is the same type-safety restriction we enforce * for regular functions, but at the level of aggregates. We must test * this explicitly because we allow INTERNAL as the transtype. */ if (finaltype == INTERNALOID && !hasInternalArg) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); /* handle sortop, if supplied */ if (aggsortopName) { if (numArgs != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("sort operator can only be specified for single-argument aggregates"))); sortop = LookupOperName(NULL, aggsortopName, aggArgTypes[0], aggArgTypes[0], false, -1); } /* * Everything looks okay. Try to create the pg_proc entry for the * aggregate. (This could fail if there's already a conflicting entry.) */ procOid = ProcedureCreate(aggName, aggNamespace, false, /* no replacement */ false, /* doesn't return a set */ finaltype, /* returnType */ INTERNALlanguageId, /* languageObjectId */ InvalidOid, /* no validator */ "aggregate_dummy", /* placeholder proc */ NULL, /* probin */ true, /* isAgg */ false, /* isWindowFunc */ false, /* security invoker (currently not * definable for agg) */ false, /* isStrict (not needed for agg) */ PROVOLATILE_IMMUTABLE, /* volatility (not * needed for agg) */ buildoidvector(aggArgTypes, numArgs), /* paramTypes */ PointerGetDatum(NULL), /* allParamTypes */ PointerGetDatum(NULL), /* parameterModes */ PointerGetDatum(NULL), /* parameterNames */ NIL, /* parameterDefaults */ PointerGetDatum(NULL), /* proconfig */ 1, /* procost */ 0); /* prorows */ /* * Okay to create the pg_aggregate entry. */ /* initialize nulls and values */ for (i = 0; i < Natts_pg_aggregate; i++) { nulls[i] = false; values[i] = (Datum) NULL; } values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid); values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn); values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn); values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop); values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType); if (agginitval) values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval); else nulls[Anum_pg_aggregate_agginitval - 1] = true; aggdesc = heap_open(AggregateRelationId, RowExclusiveLock); tupDesc = aggdesc->rd_att; tup = heap_form_tuple(tupDesc, values, nulls); simple_heap_insert(aggdesc, tup); CatalogUpdateIndexes(aggdesc, tup); heap_close(aggdesc, RowExclusiveLock); /* * Create dependencies for the aggregate (above and beyond those already * made by ProcedureCreate). Note: we don't need an explicit dependency * on aggTransType since we depend on it indirectly through transfn. */ myself.classId = ProcedureRelationId; myself.objectId = procOid; myself.objectSubId = 0; /* Depends on transition function */ referenced.classId = ProcedureRelationId; referenced.objectId = transfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* Depends on final function, if any */ if (OidIsValid(finalfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = finalfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* Depends on sort operator, if any */ if (OidIsValid(sortop)) { referenced.classId = OperatorRelationId; referenced.objectId = sortop; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } }