/* * lookup a parser support function and return its OID (as a Datum) * * attnum is the pg_ts_parser column the function will go into */ static Datum get_ts_parser_func(DefElem *defel, int attnum) { List *funcName = defGetQualifiedName(defel); Oid typeId[3]; Oid retTypeId; int nargs; Oid procOid; retTypeId = INTERNALOID; /* correct for most */ typeId[0] = INTERNALOID; switch (attnum) { case Anum_pg_ts_parser_prsstart: nargs = 2; typeId[1] = INT4OID; break; case Anum_pg_ts_parser_prstoken: nargs = 3; typeId[1] = INTERNALOID; typeId[2] = INTERNALOID; break; case Anum_pg_ts_parser_prsend: nargs = 1; retTypeId = VOIDOID; break; case Anum_pg_ts_parser_prsheadline: nargs = 3; typeId[1] = INTERNALOID; typeId[2] = TSQUERYOID; break; case Anum_pg_ts_parser_prslextype: nargs = 1; /* * Note: because the lextype method returns type internal, it must * have an internal-type argument for security reasons. The * argument is not actually used, but is just passed as a zero. */ break; default: /* should not be here */ elog(ERROR, "unrecognized attribute for text search parser: %d", attnum); nargs = 0; /* keep compiler quiet */ } procOid = LookupFuncName(funcName, nargs, typeId, false); if (get_func_rettype(procOid) != retTypeId) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("function %s should return type %s", func_signature_string(funcName, nargs, NIL, typeId), format_type_be(retTypeId)))); return ObjectIdGetDatum(procOid); }
/* * lookup_agg_function -- common code for finding both transfn and finalfn */ static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types, Oid *rettype) { Oid fnOid; bool retset; Oid *true_oid_array; FuncDetailCode fdresult; AclResult aclresult; /* * func_get_detail looks up the function in the catalogs, does * disambiguation for polymorphic functions, handles inheritance, and * returns the funcid and type and set or singleton status of the * function's return value. it also returns the true argument types * to the function. */ fdresult = func_get_detail(fnName, NIL, nargs, input_types, &fnOid, rettype, &retset, &true_oid_array); /* only valid case is a normal function not returning a set */ if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function %s does not exist", func_signature_string(fnName, nargs, input_types)))); if (retset) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function %s returns a set", func_signature_string(fnName, nargs, input_types)))); /* * If the given type(s) are all polymorphic, there's nothing we can * check. Otherwise, enforce consistency, and possibly refine the * result type. */ if ((input_types[0] == ANYARRAYOID || input_types[0] == ANYELEMENTOID) && (nargs == 1 || (input_types[1] == ANYARRAYOID || input_types[1] == ANYELEMENTOID))) { /* nothing to check here */ } else { *rettype = enforce_generic_type_consistency(input_types, true_oid_array, nargs, *rettype); } /* * func_get_detail will find functions requiring run-time argument * type coercion, but nodeAgg.c isn't prepared to deal with that */ if (true_oid_array[0] != ANYARRAYOID && true_oid_array[0] != ANYELEMENTOID && !IsBinaryCoercible(input_types[0], true_oid_array[0])) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function %s requires run-time type coercion", func_signature_string(fnName, nargs, true_oid_array)))); if (nargs == 2 && true_oid_array[1] != ANYARRAYOID && true_oid_array[1] != ANYELEMENTOID && !IsBinaryCoercible(input_types[1], true_oid_array[1])) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function %s requires run-time type coercion", func_signature_string(fnName, nargs, true_oid_array)))); /* Check aggregate creator has permission to call the function */ aclresult = pg_proc_aclcheck(fnOid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(fnOid)); return fnOid; }
/* * lookup_agg_function * common code for finding transfn, invtransfn, finalfn, and combinefn * * Returns OID of function, and stores its return type into *rettype * * NB: must not scribble on input_types[], as we may re-use those */ static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types, Oid variadicArgType, Oid *rettype) { Oid fnOid; bool retset; int nvargs; Oid vatype; Oid *true_oid_array; FuncDetailCode fdresult; AclResult aclresult; int i; /* * func_get_detail looks up the function in the catalogs, does * disambiguation for polymorphic functions, handles inheritance, and * returns the funcid and type and set or singleton status of the * function's return value. it also returns the true argument types to * the function. */ fdresult = func_get_detail(fnName, NIL, NIL, nargs, input_types, false, false, &fnOid, rettype, &retset, &nvargs, &vatype, &true_oid_array, NULL); /* only valid case is a normal function not returning a set */ if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function %s does not exist", func_signature_string(fnName, nargs, NIL, input_types)))); if (retset) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function %s returns a set", func_signature_string(fnName, nargs, NIL, input_types)))); /* * If the agg is declared to take VARIADIC ANY, the underlying functions * had better be declared that way too, else they may receive too many * parameters; but func_get_detail would have been happy with plain ANY. * (Probably nothing very bad would happen, but it wouldn't work as the * user expects.) Other combinations should work without any special * pushups, given that we told func_get_detail not to expand VARIADIC. */ if (variadicArgType == ANYOID && vatype != ANYOID) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function %s must accept VARIADIC ANY to be used in this aggregate", func_signature_string(fnName, nargs, NIL, input_types)))); /* * If there are any polymorphic types involved, enforce consistency, and * possibly refine the result type. It's OK if the result is still * polymorphic at this point, though. */ *rettype = enforce_generic_type_consistency(input_types, true_oid_array, nargs, *rettype, true); /* * func_get_detail will find functions requiring run-time argument type * coercion, but nodeAgg.c isn't prepared to deal with that */ for (i = 0; i < nargs; i++) { if (!IsBinaryCoercible(input_types[i], true_oid_array[i])) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function %s requires run-time type coercion", func_signature_string(fnName, nargs, NIL, true_oid_array)))); } /* Check aggregate creator has permission to call the function */ aclresult = pg_proc_aclcheck(fnOid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(fnOid)); return fnOid; }
/** * @brief Parse function expression */ ParsedFunction ParseFunction(const char *value, bool argistype) { int i; ParsedFunction ret; char *buf; const char *nextp; bool done = false; List *names; ListCell *l; int nargs; FuncCandidateList candidates; FuncCandidateList find = NULL; int ncandidates = 0; HeapTuple ftup; Form_pg_proc pp; AclResult aclresult; buf = palloc(strlen(value) + 1); /* parse function name */ nextp = value; do { if (*nextp == '\"') { /* Quoted name */ for (;;) { nextp = strchr(nextp + 1, '\"'); /* mismatched quotes */ if (nextp == NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("function call syntax error: %s", value))); if (nextp[1] != '\"') break; /* found end of quoted name */ } /* nextp now points at the terminating quote */ nextp = nextp + 1; } else if (IsIdentStart((unsigned char) *nextp)) { /* Unquoted name */ nextp++; while (IsIdentContent((unsigned char) *nextp)) nextp++; } else { /* invalid syntax */ ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("function call syntax error: %s", value))); } while (isspace((unsigned char) *nextp)) nextp++; /* skip trailing whitespace */ if (*nextp == '.') { nextp++; while (isspace((unsigned char) *nextp)) nextp++; /* skip leading whitespace for next */ /* we expect another name, so done remains false */ } else if (*nextp == '\0' || *nextp == '(') done = true; else { /* invalid syntax */ ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("function call syntax error: %s", value))); } /* Loop back if we didn't reach end of function name */ } while (!done); strncpy(buf, value, nextp - value); buf[nextp - value] = '\0'; names = stringToQualifiedNameList(buf); pfree(buf); if (*nextp == '\0') { if (!argistype) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("function call syntax error: %s", value))); nargs = -1; /* Get list of possible candidates from namespace search */ candidates = FuncnameGetCandidates(names, nargs, NIL, false, false); } else { /* parse function arguments */ nargs = 0; while (GetNextArgument(nextp, &ret.args[nargs], &ret.argtypes[nargs], &nextp, value, argistype)) { nargs++; if (nargs > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg("functions cannot have more than %d arguments", FUNC_MAX_ARGS))); } /* Get list of possible candidates from namespace search */ candidates = FuncnameGetCandidates(names, nargs, NIL, true, true); } /* so now try to match up candidates */ if (!argistype) { FuncCandidateList current_candidates; ncandidates = func_match_argtypes(nargs, ret.argtypes, candidates, ¤t_candidates); /* one match only? then run with it... */ if (ncandidates == 1) find = current_candidates; /* multiple candidates? then better decide or throw an error... */ else if (ncandidates > 1) { find = func_select_candidate(nargs, ret.argtypes, current_candidates); } } else if (nargs > 0) { /* Quickly check if there is an exact match to the input datatypes */ for (find = candidates; find; find = find->next) { if (memcmp(find->args, ret.argtypes, nargs * sizeof(Oid)) == 0) { ncandidates = 1; break; } } } else { FuncCandidateList c; for (c = candidates; c; c = c->next) ncandidates++; find = candidates; } if (ncandidates == 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function %s does not exist", func_signature_string(names, nargs, NIL, ret.argtypes)), errhint("No function matches the given name and argument types."))); /* * If we were able to choose a best candidate, we're done. * Otherwise, ambiguous function call. */ if (ncandidates > 1 || !OidIsValid(find->oid)) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_FUNCTION), errmsg("function %s is not unique", func_signature_string(names, nargs, NIL, ret.argtypes)), errhint("Could not choose a best candidate function."))); foreach (l, names) { Value *v = lfirst(l); pfree(strVal(v)); pfree(v); }
/* * ValidateProtocolFunction -- common code for finding readfn, writefn or validatorfn */ static Oid ValidateProtocolFunction(List *fnName, ExtPtcFuncType fntype) { Oid fnOid; bool retset; bool retstrict; bool retordered; Oid *true_oid_array; Oid actual_rettype; Oid desired_rettype; FuncDetailCode fdresult; AclResult aclresult; Oid inputTypes[1] = {InvalidOid}; /* dummy */ int nargs = 0; /* true for all 3 function types at the moment */ int nvargs; if (fntype == EXTPTC_FUNC_VALIDATOR) desired_rettype = VOIDOID; else desired_rettype = INT4OID; /* * func_get_detail looks up the function in the catalogs, does * disambiguation for polymorphic functions, handles inheritance, and * returns the funcid and type and set or singleton status of the * function's return value. it also returns the true argument types to * the function. */ fdresult = func_get_detail(fnName, NIL, nargs, inputTypes, false, false, &fnOid, &actual_rettype, &retset, &retstrict, &retordered, &nvargs, &true_oid_array, NULL); /* only valid case is a normal function not returning a set */ if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function %s does not exist", func_signature_string(fnName, nargs, inputTypes)))); if (retset) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("Invalid protocol function"), errdetail("Protocol functions cannot return sets."))); if (actual_rettype != desired_rettype) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("%s protocol function %s must return %s", func_type_to_name(fntype), func_signature_string(fnName, nargs, inputTypes), (fntype == EXTPTC_FUNC_VALIDATOR ? "void" : "an integer")))); if (func_volatile(fnOid) == PROVOLATILE_IMMUTABLE) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("%s protocol function %s is declared IMMUTABLE", func_type_to_name(fntype), func_signature_string(fnName, nargs, inputTypes)), errhint("PROTOCOL functions must be declared STABLE or VOLATILE"))); /* Check protocol creator has permission to call the function */ aclresult = pg_proc_aclcheck(fnOid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(fnOid)); return fnOid; }
/* * 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; }
/* * Parse a function call * * For historical reasons, Postgres tries to treat the notations tab.col * and col(tab) as equivalent: if a single-argument function call has an * argument of complex type and the (unqualified) function name matches * any attribute of the type, we take it as a column projection. Conversely * a function of a single complex-type argument can be written like a * column reference, allowing functions to act like computed columns. * * Hence, both cases come through here. The is_column parameter tells us * which syntactic construct is actually being dealt with, but this is * intended to be used only to deliver an appropriate error message, * not to affect the semantics. When is_column is true, we should have * a single argument (the putative table), unqualified function name * equal to the column name, and no aggregate or variadic decoration. * * The argument expressions (in fargs) must have been transformed already. */ Node * ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, bool agg_star, bool agg_distinct, bool func_variadic, WindowDef *over, bool is_column, int location) { Oid rettype; Oid funcid; ListCell *l; ListCell *nextl; Node *first_arg = NULL; int nargs; int nargsplusdefs; Oid actual_arg_types[FUNC_MAX_ARGS]; Oid *declared_arg_types; List *argdefaults; Node *retval; bool retset; int nvargs; FuncDetailCode fdresult; /* * Most of the rest of the parser just assumes that functions do not have * more than FUNC_MAX_ARGS parameters. We have to test here to protect * against array overruns, etc. Of course, this may not be a function, * but the test doesn't hurt. */ if (list_length(fargs) > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg_plural("cannot pass more than %d argument to a function", "cannot pass more than %d arguments to a function", FUNC_MAX_ARGS, FUNC_MAX_ARGS), parser_errposition(pstate, location))); /* * Extract arg type info in preparation for function lookup. * * If any arguments are Param markers of type VOID, we discard them from * the parameter list. This is a hack to allow the JDBC driver to not * have to distinguish "input" and "output" parameter symbols while * parsing function-call constructs. We can't use foreach() because we * may modify the list ... */ nargs = 0; for (l = list_head(fargs); l != NULL; l = nextl) { Node *arg = lfirst(l); Oid argtype = exprType(arg); nextl = lnext(l); if (argtype == VOIDOID && IsA(arg, Param) &&!is_column) { fargs = list_delete_ptr(fargs, arg); continue; } actual_arg_types[nargs++] = argtype; } if (fargs) { first_arg = linitial(fargs); Assert(first_arg != NULL); } /* * Check for column projection: if function has one argument, and that * argument is of complex type, and function name is not qualified, then * the "function call" could be a projection. We also check that there * wasn't any aggregate or variadic decoration. */ if (nargs == 1 && !agg_star && !agg_distinct && over == NULL && !func_variadic && list_length(funcname) == 1) { Oid argtype = actual_arg_types[0]; if (argtype == RECORDOID || ISCOMPLEX(argtype)) { retval = ParseComplexProjection(pstate, strVal(linitial(funcname)), first_arg, location); if (retval) return retval; /* * If ParseComplexProjection doesn't recognize it as a projection, * just press on. */ } } /* * Okay, it's not a column projection, so it must really be a function. * func_get_detail looks up the function in the catalogs, does * disambiguation for polymorphic functions, handles inheritance, and * returns the funcid and type and set or singleton status of the * function's return value. It also returns the true argument types to * the function. In the case of a variadic function call, the reported * "true" types aren't really what is in pg_proc: the variadic argument is * replaced by a suitable number of copies of its element type. We'll fix * it up below. We may also have to deal with default arguments. */ fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types, !func_variadic, true, &funcid, &rettype, &retset, &nvargs, &declared_arg_types, &argdefaults); if (fdresult == FUNCDETAIL_COERCION) { /* * We interpreted it as a type coercion. coerce_type can handle these * cases, so why duplicate code... */ return coerce_type(pstate, linitial(fargs), actual_arg_types[0], rettype, -1, COERCION_EXPLICIT, COERCE_EXPLICIT_CALL, location); } else if (fdresult == FUNCDETAIL_NORMAL) { /* * Normal function found; was there anything indicating it must be an * aggregate? */ if (agg_star) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("%s(*) specified, but %s is not an aggregate function", NameListToString(funcname), NameListToString(funcname)), parser_errposition(pstate, location))); if (agg_distinct) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("DISTINCT specified, but %s is not an aggregate function", NameListToString(funcname)), parser_errposition(pstate, location))); if (over) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("OVER specified, but %s is not a window function nor an aggregate function", NameListToString(funcname)), parser_errposition(pstate, location))); } else if (!(fdresult == FUNCDETAIL_AGGREGATE || fdresult == FUNCDETAIL_WINDOWFUNC)) { /* * Oops. Time to die. * * If we are dealing with the attribute notation rel.function, give an * error message that is appropriate for that case. */ if (is_column) { Assert(nargs == 1); Assert(list_length(funcname) == 1); unknown_attribute(pstate, first_arg, strVal(linitial(funcname)), location); } /* * Else generate a detailed complaint for a function */ if (fdresult == FUNCDETAIL_MULTIPLE) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_FUNCTION), errmsg("function %s is not unique", func_signature_string(funcname, nargs, actual_arg_types)), errhint("Could not choose a best candidate function. " "You might need to add explicit type casts."), parser_errposition(pstate, location))); else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function %s does not exist", func_signature_string(funcname, nargs, actual_arg_types)), errhint("No function matches the given name and argument types. " "You might need to add explicit type casts."), parser_errposition(pstate, location))); } /* * If there are default arguments, we have to include their types in * actual_arg_types for the purpose of checking generic type consistency. * However, we do NOT put them into the generated parse node, because * their actual values might change before the query gets run. The * planner has to insert the up-to-date values at plan time. */ nargsplusdefs = nargs; foreach(l, argdefaults) { Node *expr = (Node *) lfirst(l); /* probably shouldn't happen ... */ if (nargsplusdefs >= FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg_plural("cannot pass more than %d argument to a function", "cannot pass more than %d arguments to a function", FUNC_MAX_ARGS, FUNC_MAX_ARGS), parser_errposition(pstate, location))); actual_arg_types[nargsplusdefs++] = exprType(expr); }
/* * lookup_agg_function -- common code for finding both transfn and finalfn */ static Oid lookup_agg_function(List *fnName, int nargs, Oid *input_types, Oid *rettype) { Oid fnOid; bool retset; int nvargs; Oid *true_oid_array; FuncDetailCode fdresult; AclResult aclresult; int i; /* * func_get_detail looks up the function in the catalogs, does * disambiguation for polymorphic functions, handles inheritance, and * returns the funcid and type and set or singleton status of the * function's return value. it also returns the true argument types to * the function. */ fdresult = func_get_detail(fnName, NIL, nargs, input_types, false, false, &fnOid, rettype, &retset, &nvargs, &true_oid_array, NULL); /* only valid case is a normal function not returning a set */ if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function %s does not exist", func_signature_string(fnName, nargs, input_types)))); if (retset) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function %s returns a set", func_signature_string(fnName, nargs, input_types)))); /* * If there are any polymorphic types involved, enforce consistency, and * possibly refine the result type. It's OK if the result is still * polymorphic at this point, though. */ *rettype = enforce_generic_type_consistency(input_types, true_oid_array, nargs, *rettype, true); /* * func_get_detail will find functions requiring run-time argument type * coercion, but nodeAgg.c isn't prepared to deal with that */ for (i = 0; i < nargs; i++) { if (!IsPolymorphicType(true_oid_array[i]) && !IsBinaryCoercible(input_types[i], true_oid_array[i])) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function %s requires run-time type coercion", func_signature_string(fnName, nargs, true_oid_array)))); } /* Check aggregate creator has permission to call the function */ aclresult = pg_proc_aclcheck(fnOid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(fnOid)); return fnOid; }