| /*------------------------------------------------------------------------- |
| * |
| * parse_func.c |
| * handle function calls in parser |
| * |
| * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * |
| * IDENTIFICATION |
| * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.190 2006/10/04 00:29:55 momjian Exp $ |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #include "postgres.h" |
| |
| #include "access/heapam.h" |
| #include "catalog/catquery.h" |
| #include "access/transam.h" |
| #include "catalog/pg_aggregate.h" |
| #include "catalog/pg_inherits.h" |
| #include "catalog/pg_proc.h" |
| #include "catalog/pg_proc_callback.h" |
| #include "catalog/pg_type.h" |
| #include "catalog/pg_window.h" |
| #include "funcapi.h" |
| #include "nodes/makefuncs.h" |
| #include "optimizer/walkers.h" |
| #include "parser/parse_agg.h" |
| #include "parser/parse_clause.h" |
| #include "parser/parse_coerce.h" |
| #include "parser/parse_expr.h" |
| #include "parser/parse_func.h" |
| #include "parser/parse_relation.h" |
| #include "parser/parse_target.h" |
| #include "parser/parse_type.h" |
| #include "utils/builtins.h" |
| #include "utils/fmgroids.h" |
| #include "utils/lsyscache.h" |
| #include "utils/syscache.h" |
| |
| |
| static Node *ParseComplexProjection(ParseState *pstate, char *funcname, |
| Node *first_arg, int location); |
| static void unknown_attribute(ParseState *pstate, Node *relref, char *attname, |
| int location); |
| |
| typedef struct |
| { |
| Node *parent; |
| } check_table_func_context; |
| |
| static bool |
| checkTableFunctions_walker(Node *node, check_table_func_context *context); |
| |
| /* |
| * 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 decoration. |
| * |
| * The argument expressions (in fargs) must have been transformed already. |
| */ |
| Node * |
| ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, |
| List *agg_order, bool agg_star, bool agg_distinct, |
| bool is_column, WindowSpec *over, int location, |
| Node *agg_filter) |
| { |
| Oid rettype = InvalidOid; |
| Oid funcid = InvalidOid; |
| ListCell *l; |
| ListCell *nextl; |
| Node *first_arg = NULL; |
| int nargs; |
| Oid actual_arg_types[FUNC_MAX_ARGS]; |
| Oid *declared_arg_types = NULL; |
| Node *retval = NULL; |
| bool retset = false; |
| bool retstrict = false; |
| bool retordered = false; |
| 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("cannot pass more than %d arguments to a function", |
| FUNC_MAX_ARGS), |
| parser_errposition(pstate, location))); |
| |
| /* |
| * Perform the FILTER -> CASE transform. |
| * FUNC(expr) FILTER (WHERE cond) => FUNC(CASE WHEN cond THEN expr END) |
| * This must be done for every parameter of the function and special handling |
| * is needed for FUNC(*). |
| * |
| * For this to be a valid transform we must assume that NULLs passed into |
| * the function will not change the result. This assumption is not valid |
| * for count(*), which is why we need special processing for this case. If |
| * it is not a valid assumption for other cases we may need to rethink how |
| * we implement FILTER. |
| */ |
| if (agg_filter) |
| { |
| List *newfargs = NULL; |
| |
| if (agg_star || !fargs) |
| { |
| /* |
| * FUNC(*) => assume that datatype doesn't matter |
| * By converting agg_star into a conditional constant boolean |
| * expression we get the correct results for count(*) since it |
| * will then supress the NULLs returned by the CASE statement. |
| */ |
| CaseExpr *c = makeNode(CaseExpr); |
| CaseWhen *w = makeNode(CaseWhen); |
| A_Const *a = makeNode(A_Const); |
| a->val.type = T_Integer; |
| a->val.val.ival = 1; /* Actual value shouldn't matter */ |
| w->expr = (Expr *) agg_filter; |
| w->result = (Expr *) a; |
| c->casetype = InvalidOid; /* will analyze in a moment */ |
| c->arg = (Expr *) NULL; |
| c->defresult = (Expr *) NULL; |
| c->args = list_make1(w); |
| newfargs = list_make1(c); |
| |
| /* |
| * Since we haven't checked the compatability of our function with |
| * agg_star we can not clear the local bit yet, otherwise we would |
| * loose track of the fact that this was an agg_star operation prior |
| * to transformation. |
| */ |
| } |
| else |
| { |
| Assert(fargs && list_length(fargs) > 0); |
| |
| foreach(l, fargs) |
| { |
| CaseExpr *c = makeNode(CaseExpr); |
| CaseWhen *w = makeNode(CaseWhen); |
| w->expr = (Expr *) agg_filter; |
| w->result = (Expr *) lfirst(l); |
| c->casetype = InvalidOid; /* will analyze in a moment */ |
| c->arg = (Expr *) NULL; |
| c->defresult = (Expr *) NULL; |
| c->args = list_make1(w); |
| |
| if (newfargs) |
| lappend(newfargs, c); |
| else |
| newfargs = list_make1(c); |
| } |
| } |
| fargs = transformExpressionList(pstate, newfargs); |
| } |
| |
| /* |
| * 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 decoration. |
| */ |
| if (nargs == 1 && agg_order == NIL && !agg_star && !agg_distinct && |
| !agg_filter && 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. |
| */ |
| fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types, |
| &funcid, &rettype, &retset, &retstrict, |
| &retordered, &declared_arg_types); |
| if (fdresult == FUNCDETAIL_COERCION) |
| { |
| /* |
| * We can do it as a trivial 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, |
| -1); |
| } |
| 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)), |
| errOmitLocation(true), |
| parser_errposition(pstate, location))); |
| if (agg_order) |
| ereport(ERROR, |
| (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
| errmsg("ORDER BY specified, but %s is not an ordered aggregate function", |
| NameListToString(funcname)), |
| errOmitLocation(true), |
| parser_errposition(pstate, location))); |
| if (agg_filter) |
| ereport(ERROR, |
| (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
| errmsg("filter clause specified, but " |
| "%s is not an aggregate function", |
| NameListToString(funcname)), |
| errOmitLocation(true), |
| parser_errposition(pstate, location))); |
| } |
| else if (fdresult != FUNCDETAIL_AGGREGATE) |
| { |
| /* |
| * 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 may need to add explicit type casts."), |
| errOmitLocation(true), |
| 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 may need to add explicit type casts."), |
| errOmitLocation(true), |
| parser_errposition(pstate, location))); |
| } |
| |
| /* |
| * The agg_filter rewrite in the case of agg_star is only valid for count(*) |
| * otherwise we need to throw an error. |
| */ |
| if (agg_star && agg_filter && funcid != COUNT_ANY_OID) |
| { |
| ereport(ERROR, |
| (errcode(ERRCODE_UNDEFINED_FUNCTION), |
| errmsg("function %s() does not exist", |
| NameListToString(funcname)), |
| errhint("No function matches the given name and argument types. " |
| "You may need to add explicit type casts."), |
| errOmitLocation(true), |
| parser_errposition(pstate, location))); |
| } |
| |
| /* |
| * enforce consistency with ANYARRAY and ANYELEMENT argument and return |
| * types, possibly adjusting return type or declared_arg_types (which will |
| * be used as the cast destination by make_fn_arguments) |
| */ |
| rettype = enforce_generic_type_consistency(actual_arg_types, |
| declared_arg_types, |
| nargs, |
| rettype); |
| |
| /* perform the necessary typecasting of arguments */ |
| make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types); |
| |
| /* build the appropriate output structure */ |
| if (fdresult == FUNCDETAIL_NORMAL && over == NULL) |
| { |
| FuncExpr *funcexpr = makeNode(FuncExpr); |
| |
| funcexpr->funcid = funcid; |
| funcexpr->funcresulttype = rettype; |
| funcexpr->funcretset = retset; |
| funcexpr->funcformat = COERCE_EXPLICIT_CALL; |
| funcexpr->args = fargs; |
| |
| retval = (Node *) funcexpr; |
| } |
| else if(over != NULL) |
| { |
| /* must be a window function call */ |
| WindowRef *winref = makeNode(WindowRef); |
| HeapTuple tuple; |
| cqContext *wincqCtx; |
| |
| if (retset) |
| ereport(ERROR, |
| (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |
| errmsg("window functions may not return sets"), |
| parser_errposition(pstate, location))); |
| |
| if (agg_order) |
| ereport(ERROR, |
| (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
| errmsg("aggregate ORDER BY is not implemented for window functions"), |
| parser_errposition(pstate, location))); |
| |
| |
| /* |
| * If this is a "true" window function, rather than an aggregate |
| * derived window function then it will have a tuple in pg_window |
| */ |
| |
| wincqCtx = caql_beginscan( |
| NULL, |
| cql("SELECT * FROM pg_window " |
| " WHERE winfnoid = :1 ", |
| ObjectIdGetDatum(funcid))); |
| |
| tuple = caql_getnext(wincqCtx); |
| |
| if (HeapTupleIsValid(tuple)) |
| { |
| if (agg_filter) |
| ereport(ERROR, |
| (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
| errmsg("window function \"%s\" can not be used with a " |
| "filter clause", |
| NameListToString(funcname)), |
| parser_errposition(pstate, location))); |
| |
| /* |
| * We perform more checks – such as whether the window |
| * function requires ordering or permits a frame specification – |
| * later in transformWindowClause(). It's too early at this stage. |
| */ |
| |
| } |
| caql_endscan(wincqCtx); |
| |
| |
| winref->winfnoid = funcid; |
| winref->restype = rettype; |
| winref->args = fargs; |
| |
| { |
| /* |
| * Find if this "over" clause has already existed. If so, |
| * We let the "winspec" for this WindowRef point to |
| * the existing "over" clause. In this way, we will be able |
| * to determine if two WindowRef nodes are actually equal, |
| * see MPP-4268. |
| */ |
| int winspec = 0; |
| ListCell *over_lc = NULL; |
| |
| transformWindowSpec(pstate, over); |
| |
| foreach (over_lc, pstate->p_win_clauses) |
| { |
| Node *over1 = lfirst(over_lc); |
| if (equal(over1, over)) |
| break; |
| winspec++; |
| } |
| |
| if (over_lc == NULL) |
| pstate->p_win_clauses = lappend(pstate->p_win_clauses, over); |
| winref->winspec = winspec; |
| } |
| |
| winref->windistinct = agg_distinct; |
| winref->location = location; |
| |
| transformWindowFuncCall(pstate, winref); |
| retval = (Node *) winref; |
| } |
| else |
| { |
| /* aggregate function */ |
| Aggref *aggref; |
| |
| /* |
| * Reject attempt to call a parameterless aggregate without (*) |
| * syntax. This is mere pedantry but some folks insisted ... |
| */ |
| if (fargs == NIL && !agg_star) |
| ereport(ERROR, |
| (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
| errmsg("%s(*) must be used to call a parameterless aggregate function", |
| NameListToString(funcname)), |
| parser_errposition(pstate, location))); |
| |
| /* |
| * We only support FILTER clauses over STRICT aggegation functions. |
| * |
| * All built in aggregations are strict except for int2_sum, |
| * int4_sum, and int8_sum, all of which are logically strict, but are |
| * simply defined as non-strict to bootstrap their calculations. |
| * Since they are logically strict we will not change their results |
| * by including extra nulls in the calculation so the rewrite won't |
| * produce incorrect results. |
| * |
| * For user defined functions we must enforce this restriction since |
| * passing "extra" nulls back to a non-strict function may cause it |
| * to return an incorrect answer, eg: count_null(i) filter (...) |
| * wouldn't differeniate between data nulls vs filtered values. |
| */ |
| if (agg_filter && !retstrict && |
| (funcid < SUM_OID_MIN || funcid > SUM_OID_MAX)) |
| { |
| ereport(ERROR, |
| (errcode(ERRCODE_GP_FEATURE_NOT_SUPPORTED), |
| errmsg("function %s is not defined as STRICT", |
| func_signature_string(funcname, nargs, |
| actual_arg_types)), |
| errhint("The filter clause is only supported over functions " |
| "defined as STRICT."), |
| errOmitLocation(true))); |
| } |
| |
| if (retset) |
| ereport(ERROR, |
| (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), |
| errmsg("aggregates may not return sets"), |
| parser_errposition(pstate, location), |
| errOmitLocation(true))); |
| |
| /* |
| * If this is not an ordered aggregate, but it was called with an |
| * aggregate order by specification then we must raise an error. |
| */ |
| if (!retordered && agg_order != NIL) |
| { |
| ereport(ERROR, |
| (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
| errmsg("ORDER BY specified, but %s is not an ordered aggregate function", |
| NameListToString(funcname)), |
| errOmitLocation(true), |
| parser_errposition(pstate, location))); |
| } |
| |
| /* |
| * ordered aggregates are not compatible with distinct |
| */ |
| if (agg_distinct && agg_order != NIL) |
| { |
| ereport(ERROR, |
| (errcode(ERRCODE_GP_FEATURE_NOT_SUPPORTED), |
| errmsg("ORDER BY and DISTINCT are mutually exclusive"), |
| errOmitLocation(true), |
| parser_errposition(pstate, location))); |
| } |
| |
| /* |
| * Build the aggregate node and transform it |
| * |
| * Note: aggorder is handled inside transformAggregateCall() |
| */ |
| aggref = makeNode(Aggref); |
| aggref->aggfnoid = funcid; |
| aggref->aggtype = rettype; |
| aggref->args = fargs; |
| aggref->aggstar = agg_star; |
| aggref->aggdistinct = agg_distinct; |
| |
| transformAggregateCall(pstate, aggref, agg_order); |
| |
| retval = (Node *) aggref; |
| } |
| |
| /* |
| * Mark the context if this is a dynamic typed function, if so we mustn't |
| * allow views to be created from this statement because we cannot |
| * guarantee that the future return type will be the same as the current |
| * return type. |
| */ |
| if (TypeSupportsDescribe(rettype)) |
| { |
| Oid DescribeFuncOid = lookupProcCallback(funcid, PROMETHOD_DESCRIBE); |
| if (OidIsValid(DescribeFuncOid)) |
| { |
| ParseState *state = pstate; |
| |
| for (state = pstate; state; state = state->parentParseState) |
| state->p_hasDynamicFunction = true; |
| } |
| } |
| |
| return retval; |
| } |
| |
| |
| /* func_match_argtypes() |
| * |
| * Given a list of candidate functions (having the right name and number |
| * of arguments) and an array of input datatype OIDs, produce a shortlist of |
| * those candidates that actually accept the input datatypes (either exactly |
| * or by coercion), and return the number of such candidates. |
| * |
| * Note that can_coerce_type will assume that UNKNOWN inputs are coercible to |
| * anything, so candidates will not be eliminated on that basis. |
| * |
| * NB: okay to modify input list structure, as long as we find at least |
| * one match. If no match at all, the list must remain unmodified. |
| */ |
| int |
| func_match_argtypes(int nargs, |
| Oid *input_typeids, |
| FuncCandidateList raw_candidates, |
| FuncCandidateList *candidates) /* return value */ |
| { |
| FuncCandidateList current_candidate; |
| FuncCandidateList next_candidate; |
| int ncandidates = 0; |
| |
| *candidates = NULL; |
| |
| for (current_candidate = raw_candidates; |
| current_candidate != NULL; |
| current_candidate = next_candidate) |
| { |
| next_candidate = current_candidate->next; |
| if (can_coerce_type(nargs, input_typeids, current_candidate->args, |
| COERCION_IMPLICIT)) |
| { |
| current_candidate->next = *candidates; |
| *candidates = current_candidate; |
| ncandidates++; |
| } |
| } |
| |
| return ncandidates; |
| } /* func_match_argtypes() */ |
| |
| |
| /* func_select_candidate() |
| * Given the input argtype array and more than one candidate |
| * for the function, attempt to resolve the conflict. |
| * |
| * Returns the selected candidate if the conflict can be resolved, |
| * otherwise returns NULL. |
| * |
| * Note that the caller has already determined that there is no candidate |
| * exactly matching the input argtypes, and has pruned away any "candidates" |
| * that aren't actually coercion-compatible with the input types. |
| * |
| * This is also used for resolving ambiguous operator references. Formerly |
| * parse_oper.c had its own, essentially duplicate code for the purpose. |
| * The following comments (formerly in parse_oper.c) are kept to record some |
| * of the history of these heuristics. |
| * |
| * OLD COMMENTS: |
| * |
| * This routine is new code, replacing binary_oper_select_candidate() |
| * which dates from v4.2/v1.0.x days. It tries very hard to match up |
| * operators with types, including allowing type coercions if necessary. |
| * The important thing is that the code do as much as possible, |
| * while _never_ doing the wrong thing, where "the wrong thing" would |
| * be returning an operator when other better choices are available, |
| * or returning an operator which is a non-intuitive possibility. |
| * - thomas 1998-05-21 |
| * |
| * The comments below came from binary_oper_select_candidate(), and |
| * illustrate the issues and choices which are possible: |
| * - thomas 1998-05-20 |
| * |
| * current wisdom holds that the default operator should be one in which |
| * both operands have the same type (there will only be one such |
| * operator) |
| * |
| * 7.27.93 - I have decided not to do this; it's too hard to justify, and |
| * it's easy enough to typecast explicitly - avi |
| * [the rest of this routine was commented out since then - ay] |
| * |
| * 6/23/95 - I don't complete agree with avi. In particular, casting |
| * floats is a pain for users. Whatever the rationale behind not doing |
| * this is, I need the following special case to work. |
| * |
| * In the WHERE clause of a query, if a float is specified without |
| * quotes, we treat it as float8. I added the float48* operators so |
| * that we can operate on float4 and float8. But now we have more than |
| * one matching operator if the right arg is unknown (eg. float |
| * specified with quotes). This break some stuff in the regression |
| * test where there are floats in quotes not properly casted. Below is |
| * the solution. In addition to requiring the operator operates on the |
| * same type for both operands [as in the code Avi originally |
| * commented out], we also require that the operators be equivalent in |
| * some sense. (see equivalentOpersAfterPromotion for details.) |
| * - ay 6/95 |
| */ |
| FuncCandidateList |
| func_select_candidate(int nargs, |
| Oid *input_typeids, |
| FuncCandidateList candidates) |
| { |
| FuncCandidateList current_candidate; |
| FuncCandidateList last_candidate; |
| Oid *current_typeids; |
| Oid current_type; |
| int i; |
| int ncandidates; |
| int nbestMatch, |
| nmatch; |
| Oid input_base_typeids[FUNC_MAX_ARGS]; |
| CATEGORY slot_category[FUNC_MAX_ARGS], |
| current_category; |
| bool slot_has_preferred_type[FUNC_MAX_ARGS]; |
| bool resolved_unknowns; |
| |
| /* protect local fixed-size arrays */ |
| if (nargs > FUNC_MAX_ARGS) |
| ereport(ERROR, |
| (errcode(ERRCODE_TOO_MANY_ARGUMENTS), |
| errmsg("cannot pass more than %d arguments to a function", |
| FUNC_MAX_ARGS))); |
| |
| /* |
| * If any input types are domains, reduce them to their base types. This |
| * ensures that we will consider functions on the base type to be "exact |
| * matches" in the exact-match heuristic; it also makes it possible to do |
| * something useful with the type-category heuristics. Note that this |
| * makes it difficult, but not impossible, to use functions declared to |
| * take a domain as an input datatype. Such a function will be selected |
| * over the base-type function only if it is an exact match at all |
| * argument positions, and so was already chosen by our caller. |
| */ |
| for (i = 0; i < nargs; i++) |
| input_base_typeids[i] = getBaseType(input_typeids[i]); |
| |
| /* |
| * Run through all candidates and keep those with the most matches on |
| * exact types. Keep all candidates if none match. |
| */ |
| ncandidates = 0; |
| nbestMatch = 0; |
| last_candidate = NULL; |
| for (current_candidate = candidates; |
| current_candidate != NULL; |
| current_candidate = current_candidate->next) |
| { |
| current_typeids = current_candidate->args; |
| nmatch = 0; |
| for (i = 0; i < nargs; i++) |
| { |
| if (input_base_typeids[i] != UNKNOWNOID && |
| current_typeids[i] == input_base_typeids[i]) |
| nmatch++; |
| } |
| |
| /* take this one as the best choice so far? */ |
| if ((nmatch > nbestMatch) || (last_candidate == NULL)) |
| { |
| nbestMatch = nmatch; |
| candidates = current_candidate; |
| last_candidate = current_candidate; |
| ncandidates = 1; |
| } |
| /* no worse than the last choice, so keep this one too? */ |
| else if (nmatch == nbestMatch) |
| { |
| last_candidate->next = current_candidate; |
| last_candidate = current_candidate; |
| ncandidates++; |
| } |
| /* otherwise, don't bother keeping this one... */ |
| } |
| |
| if (last_candidate) /* terminate rebuilt list */ |
| last_candidate->next = NULL; |
| |
| if (ncandidates == 1) |
| return candidates; |
| |
| /* |
| * Still too many candidates? Now look for candidates which have either |
| * exact matches or preferred types at the args that will require |
| * coercion. (Restriction added in 7.4: preferred type must be of same |
| * category as input type; give no preference to cross-category |
| * conversions to preferred types.) Keep all candidates if none match. |
| */ |
| for (i = 0; i < nargs; i++) /* avoid multiple lookups */ |
| slot_category[i] = TypeCategory(input_base_typeids[i]); |
| ncandidates = 0; |
| nbestMatch = 0; |
| last_candidate = NULL; |
| for (current_candidate = candidates; |
| current_candidate != NULL; |
| current_candidate = current_candidate->next) |
| { |
| current_typeids = current_candidate->args; |
| nmatch = 0; |
| for (i = 0; i < nargs; i++) |
| { |
| if (input_base_typeids[i] != UNKNOWNOID) |
| { |
| if (current_typeids[i] == input_base_typeids[i] || |
| IsPreferredType(slot_category[i], current_typeids[i])) |
| nmatch++; |
| } |
| } |
| |
| if ((nmatch > nbestMatch) || (last_candidate == NULL)) |
| { |
| nbestMatch = nmatch; |
| candidates = current_candidate; |
| last_candidate = current_candidate; |
| ncandidates = 1; |
| } |
| else if (nmatch == nbestMatch) |
| { |
| last_candidate->next = current_candidate; |
| last_candidate = current_candidate; |
| ncandidates++; |
| } |
| } |
| |
| if (last_candidate) /* terminate rebuilt list */ |
| last_candidate->next = NULL; |
| |
| if (ncandidates == 1) |
| return candidates; |
| |
| /* |
| * Still too many candidates? Try assigning types for the unknown columns. |
| * |
| * NOTE: for a binary operator with one unknown and one non-unknown input, |
| * we already tried the heuristic of looking for a candidate with the |
| * known input type on both sides (see binary_oper_exact()). That's |
| * essentially a special case of the general algorithm we try next. |
| * |
| * We do this by examining each unknown argument position to see if we can |
| * determine a "type category" for it. If any candidate has an input |
| * datatype of STRING category, use STRING category (this bias towards |
| * STRING is appropriate since unknown-type literals look like strings). |
| * Otherwise, if all the candidates agree on the type category of this |
| * argument position, use that category. Otherwise, fail because we |
| * cannot determine a category. |
| * |
| * If we are able to determine a type category, also notice whether any of |
| * the candidates takes a preferred datatype within the category. |
| * |
| * Having completed this examination, remove candidates that accept the |
| * wrong category at any unknown position. Also, if at least one |
| * candidate accepted a preferred type at a position, remove candidates |
| * that accept non-preferred types. |
| * |
| * If we are down to one candidate at the end, we win. |
| */ |
| resolved_unknowns = false; |
| for (i = 0; i < nargs; i++) |
| { |
| bool have_conflict; |
| |
| if (input_base_typeids[i] != UNKNOWNOID) |
| continue; |
| resolved_unknowns = true; /* assume we can do it */ |
| slot_category[i] = INVALID_TYPE; |
| slot_has_preferred_type[i] = false; |
| have_conflict = false; |
| for (current_candidate = candidates; |
| current_candidate != NULL; |
| current_candidate = current_candidate->next) |
| { |
| current_typeids = current_candidate->args; |
| current_type = current_typeids[i]; |
| current_category = TypeCategory(current_type); |
| if (slot_category[i] == INVALID_TYPE) |
| { |
| /* first candidate */ |
| slot_category[i] = current_category; |
| slot_has_preferred_type[i] = |
| IsPreferredType(current_category, current_type); |
| } |
| else if (current_category == slot_category[i]) |
| { |
| /* more candidates in same category */ |
| slot_has_preferred_type[i] |= |
| IsPreferredType(current_category, current_type); |
| } |
| else |
| { |
| /* category conflict! */ |
| if (current_category == STRING_TYPE) |
| { |
| /* STRING always wins if available */ |
| slot_category[i] = current_category; |
| slot_has_preferred_type[i] = |
| IsPreferredType(current_category, current_type); |
| } |
| else |
| { |
| /* |
| * Remember conflict, but keep going (might find STRING) |
| */ |
| have_conflict = true; |
| } |
| } |
| } |
| if (have_conflict && slot_category[i] != STRING_TYPE) |
| { |
| /* Failed to resolve category conflict at this position */ |
| resolved_unknowns = false; |
| break; |
| } |
| } |
| |
| if (resolved_unknowns) |
| { |
| /* Strip non-matching candidates */ |
| ncandidates = 0; |
| last_candidate = NULL; |
| for (current_candidate = candidates; |
| current_candidate != NULL; |
| current_candidate = current_candidate->next) |
| { |
| bool keepit = true; |
| |
| current_typeids = current_candidate->args; |
| for (i = 0; i < nargs; i++) |
| { |
| if (input_base_typeids[i] != UNKNOWNOID) |
| continue; |
| current_type = current_typeids[i]; |
| current_category = TypeCategory(current_type); |
| if (current_category != slot_category[i]) |
| { |
| keepit = false; |
| break; |
| } |
| if (slot_has_preferred_type[i] && |
| !IsPreferredType(current_category, current_type)) |
| { |
| keepit = false; |
| break; |
| } |
| } |
| if (keepit) |
| { |
| /* keep this candidate */ |
| last_candidate = current_candidate; |
| ncandidates++; |
| } |
| else |
| { |
| /* forget this candidate */ |
| if (last_candidate) |
| last_candidate->next = current_candidate->next; |
| else |
| candidates = current_candidate->next; |
| } |
| } |
| if (last_candidate) /* terminate rebuilt list */ |
| last_candidate->next = NULL; |
| } |
| |
| if (ncandidates == 1) |
| return candidates; |
| |
| return NULL; /* failed to select a best candidate */ |
| } /* func_select_candidate() */ |
| |
| |
| /* func_get_detail() |
| * |
| * Find the named function in the system catalogs. |
| * |
| * Attempt to find the named function in the system catalogs with |
| * arguments exactly as specified, so that the normal case |
| * (exact match) is as quick as possible. |
| * |
| * If an exact match isn't found: |
| * 1) check for possible interpretation as a trivial type coercion |
| * 2) get a vector of all possible input arg type arrays constructed |
| * from the superclasses of the original input arg types |
| * 3) get a list of all possible argument type arrays to the function |
| * with given name and number of arguments |
| * 4) for each input arg type array from vector #1: |
| * a) find how many of the function arg type arrays from list #2 |
| * it can be coerced to |
| * b) if the answer is one, we have our function |
| * c) if the answer is more than one, attempt to resolve the conflict |
| * d) if the answer is zero, try the next array from vector #1 |
| * |
| * Note: we rely primarily on nargs/argtypes as the argument description. |
| * The actual expression node list is passed in fargs so that we can check |
| * for type coercion of a constant. Some callers pass fargs == NIL |
| * indicating they don't want that check made. |
| */ |
| FuncDetailCode |
| func_get_detail(List *funcname, |
| List *fargs, |
| int nargs, |
| Oid *argtypes, |
| Oid *funcid, /* return value */ |
| Oid *rettype, /* return value */ |
| bool *retset, /* return value */ |
| bool *retstrict, /* return value */ |
| bool *retordered, /* return value */ |
| Oid **true_typeids) /* return value */ |
| { |
| FuncCandidateList raw_candidates; |
| FuncCandidateList best_candidate; |
| |
| /* Get list of possible candidates from namespace search */ |
| raw_candidates = FuncnameGetCandidates(funcname, nargs); |
| |
| /* |
| * Quickly check if there is an exact match to the input datatypes (there |
| * can be only one) |
| */ |
| for (best_candidate = raw_candidates; |
| best_candidate != NULL; |
| best_candidate = best_candidate->next) |
| { |
| if (memcmp(argtypes, best_candidate->args, nargs * sizeof(Oid)) == 0) |
| break; |
| } |
| |
| if (best_candidate == NULL) |
| { |
| /* |
| * If we didn't find an exact match, next consider the possibility |
| * that this is really a type-coercion request: a single-argument |
| * function call where the function name is a type name. If so, and |
| * if we can do the coercion trivially (no run-time function call |
| * needed), then go ahead and treat the "function call" as a coercion. |
| * This interpretation needs to be given higher priority than |
| * interpretations involving a type coercion followed by a function |
| * call, otherwise we can produce surprising results. For example, we |
| * want "text(varchar)" to be interpreted as a trivial coercion, not |
| * as "text(name(varchar))" which the code below this point is |
| * entirely capable of selecting. |
| * |
| * "Trivial" coercions are ones that involve binary-compatible types |
| * and ones that are coercing a previously-unknown-type literal |
| * constant to a specific type. |
| * |
| * The reason we can restrict our check to binary-compatible coercions |
| * here is that we expect non-binary-compatible coercions to have an |
| * implementation function named after the target type. That function |
| * will be found by normal lookup if appropriate. |
| * |
| * NB: it's important that this code stays in sync with what |
| * coerce_type can do, because the caller will try to apply |
| * coerce_type if we return FUNCDETAIL_COERCION. If we return that |
| * result for something coerce_type can't handle, we'll cause infinite |
| * recursion between this module and coerce_type! |
| */ |
| if (nargs == 1 && fargs != NIL) |
| { |
| Oid targetType; |
| |
| targetType = LookupTypeName(NULL, |
| makeTypeNameFromNameList(funcname)); |
| if (OidIsValid(targetType) && |
| !ISCOMPLEX(targetType)) |
| { |
| Oid sourceType = argtypes[0]; |
| Node *arg1 = linitial(fargs); |
| Oid cfuncid; |
| |
| if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) || |
| (find_coercion_pathway(targetType, sourceType, |
| COERCION_EXPLICIT, &cfuncid) && |
| cfuncid == InvalidOid)) |
| { |
| /* Yup, it's a type coercion */ |
| *funcid = InvalidOid; |
| *rettype = targetType; |
| *retset = false; |
| *retstrict = false; |
| *retordered = false; |
| *true_typeids = argtypes; |
| return FUNCDETAIL_COERCION; |
| } |
| } |
| } |
| |
| /* |
| * didn't find an exact match, so now try to match up candidates... |
| */ |
| if (raw_candidates != NULL) |
| { |
| FuncCandidateList current_candidates; |
| int ncandidates; |
| |
| ncandidates = func_match_argtypes(nargs, |
| argtypes, |
| raw_candidates, |
| ¤t_candidates); |
| |
| /* one match only? then run with it... */ |
| if (ncandidates == 1) |
| best_candidate = current_candidates; |
| |
| /* |
| * multiple candidates? then better decide or throw an error... |
| */ |
| else if (ncandidates > 1) |
| { |
| best_candidate = func_select_candidate(nargs, |
| argtypes, |
| current_candidates); |
| |
| /* |
| * If we were able to choose a best candidate, we're done. |
| * Otherwise, ambiguous function call. |
| */ |
| if (!best_candidate) |
| return FUNCDETAIL_MULTIPLE; |
| } |
| } |
| } |
| |
| if (best_candidate) |
| { |
| HeapTuple ftup; |
| Form_pg_proc pform; |
| bool isagg = false; |
| cqContext *procqCtx; |
| |
| *funcid = best_candidate->oid; |
| *true_typeids = best_candidate->args; |
| |
| procqCtx = caql_beginscan( |
| NULL, |
| cql("SELECT * FROM pg_proc " |
| " WHERE oid = :1 ", |
| ObjectIdGetDatum(best_candidate->oid))); |
| |
| ftup = caql_getnext(procqCtx); |
| |
| if (!HeapTupleIsValid(ftup)) /* should not happen */ |
| elog(ERROR, "cache lookup failed for function %u", |
| best_candidate->oid); |
| pform = (Form_pg_proc) GETSTRUCT(ftup); |
| *rettype = pform->prorettype; |
| *retset = pform->proretset; |
| *retstrict = pform->proisstrict; |
| *retordered = false; |
| isagg = pform->proisagg; |
| |
| caql_endscan(procqCtx); |
| |
| /* |
| * For aggregate functions STRICTness is defined by the |
| * transition function |
| */ |
| if (isagg) |
| { |
| Form_pg_aggregate aggform; |
| FmgrInfo transfn; |
| Datum value; |
| bool isnull; |
| cqContext *aggcqCtx; |
| |
| aggcqCtx = caql_beginscan( |
| NULL, |
| cql("SELECT * FROM pg_aggregate " |
| " WHERE aggfnoid = :1 ", |
| ObjectIdGetDatum(best_candidate->oid))); |
| |
| ftup = caql_getnext(aggcqCtx); |
| |
| if (!HeapTupleIsValid(ftup)) /* should not happen */ |
| elog(ERROR, "cache lookup failed for aggregate %u", |
| best_candidate->oid); |
| aggform = (Form_pg_aggregate) GETSTRUCT(ftup); |
| fmgr_info(aggform->aggtransfn, &transfn); |
| *retstrict = transfn.fn_strict; |
| |
| /* |
| * Check if this is an ordered aggregate - while aggordered |
| * should never be null it comes after a variable length field |
| * so we must access it via caql_getattr. |
| */ |
| value = caql_getattr(aggcqCtx, |
| Anum_pg_aggregate_aggordered, |
| &isnull); |
| *retordered = (!isnull) && DatumGetBool(value); |
| |
| caql_endscan(aggcqCtx); |
| |
| return FUNCDETAIL_AGGREGATE; |
| } |
| return FUNCDETAIL_NORMAL; |
| } |
| |
| return FUNCDETAIL_NOTFOUND; |
| } |
| |
| |
| /* |
| * Given two type OIDs, determine whether the first is a complex type |
| * (class type) that inherits from the second. |
| */ |
| bool |
| typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId) |
| { |
| bool result = false; |
| Oid relid; |
| Relation inhrel; |
| List *visited, |
| *queue; |
| ListCell *queue_item; |
| |
| if (!ISCOMPLEX(subclassTypeId) || !ISCOMPLEX(superclassTypeId)) |
| return false; |
| relid = typeidTypeRelid(subclassTypeId); |
| if (relid == InvalidOid) |
| return false; |
| |
| /* |
| * Begin the search at the relation itself, so add relid to the queue. |
| */ |
| queue = list_make1_oid(relid); |
| visited = NIL; |
| |
| inhrel = heap_open(InheritsRelationId, AccessShareLock); |
| |
| /* |
| * Use queue to do a breadth-first traversal of the inheritance graph from |
| * the relid supplied up to the root. Notice that we append to the queue |
| * inside the loop --- this is okay because the foreach() macro doesn't |
| * advance queue_item until the next loop iteration begins. |
| */ |
| foreach(queue_item, queue) |
| { |
| Oid this_relid = lfirst_oid(queue_item); |
| cqContext *pcqCtx; |
| cqContext cqc; |
| HeapTuple inhtup; |
| |
| /* If we've seen this relid already, skip it */ |
| if (list_member_oid(visited, this_relid)) |
| continue; |
| |
| /* |
| * Okay, this is a not-yet-seen relid. Add it to the list of |
| * already-visited OIDs, then find all the types this relid inherits |
| * from and add them to the queue. The one exception is we don't add |
| * the original relation to 'visited'. |
| */ |
| if (queue_item != list_head(queue)) |
| visited = lappend_oid(visited, this_relid); |
| |
| pcqCtx = caql_beginscan( |
| caql_addrel(cqclr(&cqc), inhrel), |
| cql("SELECT * FROM pg_inherits " |
| " WHERE inhrelid = :1 ", |
| ObjectIdGetDatum(this_relid))); |
| |
| while (HeapTupleIsValid(inhtup = caql_getnext(pcqCtx))) |
| { |
| Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inhtup); |
| Oid inhparent = inh->inhparent; |
| |
| /* If this is the target superclass, we're done */ |
| if (get_rel_type_id(inhparent) == superclassTypeId) |
| { |
| result = true; |
| break; |
| } |
| |
| /* Else add to queue */ |
| queue = lappend_oid(queue, inhparent); |
| } |
| |
| caql_endscan(pcqCtx); |
| |
| if (result) |
| break; |
| } |
| |
| heap_close(inhrel, AccessShareLock); |
| |
| list_free(visited); |
| list_free(queue); |
| |
| return result; |
| } |
| |
| |
| /* |
| * make_fn_arguments() |
| * |
| * Given the actual argument expressions for a function, and the desired |
| * input types for the function, add any necessary typecasting to the |
| * expression tree. Caller should already have verified that casting is |
| * allowed. |
| * |
| * Caution: given argument list is modified in-place. |
| * |
| * As with coerce_type, pstate may be NULL if no special unknown-Param |
| * processing is wanted. |
| */ |
| void |
| make_fn_arguments(ParseState *pstate, |
| List *fargs, |
| Oid *actual_arg_types, |
| Oid *declared_arg_types) |
| { |
| ListCell *current_fargs; |
| int i = 0; |
| |
| foreach(current_fargs, fargs) |
| { |
| /* types don't match? then force coercion using a function call... */ |
| if (actual_arg_types[i] != declared_arg_types[i]) |
| { |
| lfirst(current_fargs) = coerce_type(pstate, |
| lfirst(current_fargs), |
| actual_arg_types[i], |
| declared_arg_types[i], -1, |
| COERCION_IMPLICIT, |
| COERCE_IMPLICIT_CAST, |
| -1); |
| } |
| i++; |
| } |
| } |
| |
| /* |
| * ParseComplexProjection - |
| * handles function calls with a single argument that is of complex type. |
| * If the function call is actually a column projection, return a suitably |
| * transformed expression tree. If not, return NULL. |
| */ |
| static Node * |
| ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg, |
| int location) |
| { |
| TupleDesc tupdesc; |
| int i; |
| |
| /* |
| * Special case for whole-row Vars so that we can resolve (foo.*).bar even |
| * when foo is a reference to a subselect, join, or RECORD function. A |
| * bonus is that we avoid generating an unnecessary FieldSelect; our |
| * result can omit the whole-row Var and just be a Var for the selected |
| * field. |
| * |
| * This case could be handled by expandRecordVariable, but it's more |
| * efficient to do it this way when possible. |
| */ |
| if (IsA(first_arg, Var) && |
| ((Var *) first_arg)->varattno == InvalidAttrNumber) |
| { |
| RangeTblEntry *rte; |
| |
| rte = GetRTEByRangeTablePosn(pstate, |
| ((Var *) first_arg)->varno, |
| ((Var *) first_arg)->varlevelsup); |
| /* Return a Var if funcname matches a column, else NULL */ |
| return scanRTEForColumn(pstate, rte, funcname, location); |
| } |
| |
| /* |
| * Else do it the hard way with get_expr_result_type(). |
| * |
| * If it's a Var of type RECORD, we have to work even harder: we have to |
| * find what the Var refers to, and pass that to get_expr_result_type. |
| * That task is handled by expandRecordVariable(). |
| */ |
| if (IsA(first_arg, Var) && |
| ((Var *) first_arg)->vartype == RECORDOID) |
| tupdesc = expandRecordVariable(pstate, (Var *) first_arg, 0); |
| else if (get_expr_result_type(first_arg, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) |
| return NULL; /* unresolvable RECORD type */ |
| Assert(tupdesc); |
| |
| for (i = 0; i < tupdesc->natts; i++) |
| { |
| Form_pg_attribute att = tupdesc->attrs[i]; |
| |
| if (strcmp(funcname, NameStr(att->attname)) == 0 && |
| !att->attisdropped) |
| { |
| /* Success, so generate a FieldSelect expression */ |
| FieldSelect *fselect = makeNode(FieldSelect); |
| |
| fselect->arg = (Expr *) first_arg; |
| fselect->fieldnum = i + 1; |
| fselect->resulttype = att->atttypid; |
| fselect->resulttypmod = att->atttypmod; |
| return (Node *) fselect; |
| } |
| } |
| |
| return NULL; /* funcname does not match any column */ |
| } |
| |
| /* |
| * helper routine for delivering "column does not exist" error message |
| */ |
| static void |
| unknown_attribute(ParseState *pstate, Node *relref, char *attname, |
| int location) |
| { |
| RangeTblEntry *rte; |
| |
| if (IsA(relref, Var) && |
| ((Var *) relref)->varattno == InvalidAttrNumber) |
| { |
| /* Reference the RTE by alias not by actual table name */ |
| rte = GetRTEByRangeTablePosn(pstate, |
| ((Var *) relref)->varno, |
| ((Var *) relref)->varlevelsup); |
| ereport(ERROR, |
| (errcode(ERRCODE_UNDEFINED_COLUMN), |
| errmsg("column %s.%s does not exist", |
| rte->eref->aliasname, attname), |
| errOmitLocation(true), |
| parser_errposition(pstate, location))); |
| } |
| else |
| { |
| /* Have to do it by reference to the type of the expression */ |
| Oid relTypeId = exprType(relref); |
| |
| if (ISCOMPLEX(relTypeId)) |
| ereport(ERROR, |
| (errcode(ERRCODE_UNDEFINED_COLUMN), |
| errmsg("column \"%s\" not found in data type %s", |
| attname, format_type_be(relTypeId)), |
| errOmitLocation(true), |
| parser_errposition(pstate, location))); |
| else if (relTypeId == RECORDOID) |
| ereport(ERROR, |
| (errcode(ERRCODE_UNDEFINED_COLUMN), |
| errmsg("could not identify column \"%s\" in record data type", |
| attname), |
| errOmitLocation(true), |
| parser_errposition(pstate, location))); |
| else |
| ereport(ERROR, |
| (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
| errmsg("column notation .%s applied to type %s, " |
| "which is not a composite type", |
| attname, format_type_be(relTypeId)), |
| errOmitLocation(true), |
| parser_errposition(pstate, location))); |
| } |
| } |
| |
| /* |
| * funcname_signature_string |
| * Build a string representing a function name, including arg types. |
| * The result is something like "foo(integer)". |
| * |
| * This is typically used in the construction of function-not-found error |
| * messages. |
| */ |
| const char * |
| funcname_signature_string(const char *funcname, |
| int nargs, const Oid *argtypes) |
| { |
| StringInfoData argbuf; |
| int i; |
| |
| initStringInfo(&argbuf); |
| |
| appendStringInfo(&argbuf, "%s(", funcname); |
| |
| for (i = 0; i < nargs; i++) |
| { |
| if (i) |
| appendStringInfoString(&argbuf, ", "); |
| appendStringInfoString(&argbuf, format_type_be(argtypes[i])); |
| } |
| |
| appendStringInfoChar(&argbuf, ')'); |
| |
| return argbuf.data; /* return palloc'd string buffer */ |
| } |
| |
| /* |
| * func_signature_string |
| * As above, but function name is passed as a qualified name list. |
| */ |
| const char * |
| func_signature_string(List *funcname, int nargs, const Oid *argtypes) |
| { |
| return funcname_signature_string(NameListToString(funcname), |
| nargs, argtypes); |
| } |
| |
| /* |
| * LookupFuncName |
| * Given a possibly-qualified function name and a set of argument types, |
| * look up the function. |
| * |
| * If the function name is not schema-qualified, it is sought in the current |
| * namespace search path. |
| * |
| * If the function is not found, we return InvalidOid if noError is true, |
| * else raise an error. |
| */ |
| Oid |
| LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError) |
| { |
| FuncCandidateList clist; |
| |
| clist = FuncnameGetCandidates(funcname, nargs); |
| |
| while (clist) |
| { |
| if (memcmp(argtypes, clist->args, nargs * sizeof(Oid)) == 0) |
| return clist->oid; |
| clist = clist->next; |
| } |
| |
| if (!noError) |
| ereport(ERROR, |
| (errcode(ERRCODE_UNDEFINED_FUNCTION), |
| errmsg("function %s does not exist", |
| func_signature_string(funcname, nargs, argtypes)), |
| errOmitLocation(true))); |
| |
| return InvalidOid; |
| } |
| |
| /* |
| * LookupFuncNameTypeNames |
| * Like LookupFuncName, but the argument types are specified by a |
| * list of TypeName nodes. |
| */ |
| Oid |
| LookupFuncNameTypeNames(List *funcname, List *argtypes, bool noError) |
| { |
| Oid argoids[FUNC_MAX_ARGS]; |
| int argcount; |
| int i; |
| ListCell *args_item; |
| |
| argcount = list_length(argtypes); |
| if (argcount > FUNC_MAX_ARGS) |
| ereport(ERROR, |
| (errcode(ERRCODE_TOO_MANY_ARGUMENTS), |
| errmsg("functions cannot have more than %d arguments", |
| FUNC_MAX_ARGS))); |
| |
| args_item = list_head(argtypes); |
| for (i = 0; i < argcount; i++) |
| { |
| TypeName *t = (TypeName *) lfirst(args_item); |
| |
| argoids[i] = LookupTypeName(NULL, t); |
| |
| if (!OidIsValid(argoids[i])) |
| ereport(ERROR, |
| (errcode(ERRCODE_UNDEFINED_OBJECT), |
| errmsg("type \"%s\" does not exist", |
| TypeNameToString(t)), |
| errOmitLocation(true))); |
| |
| args_item = lnext(args_item); |
| } |
| |
| return LookupFuncName(funcname, argcount, argoids, noError); |
| } |
| |
| /* |
| * LookupAggNameTypeNames |
| * Find an aggregate function given a name and list of TypeName nodes. |
| * |
| * This is almost like LookupFuncNameTypeNames, but the error messages refer |
| * to aggregates rather than plain functions, and we verify that the found |
| * function really is an aggregate. |
| */ |
| Oid |
| LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) |
| { |
| Oid argoids[FUNC_MAX_ARGS]; |
| int argcount; |
| int i; |
| ListCell *lc; |
| Oid oid; |
| HeapTuple ftup; |
| Form_pg_proc pform; |
| cqContext *pcqCtx; |
| bool proisagg; |
| |
| argcount = list_length(argtypes); |
| if (argcount > FUNC_MAX_ARGS) |
| ereport(ERROR, |
| (errcode(ERRCODE_TOO_MANY_ARGUMENTS), |
| errmsg("functions cannot have more than %d arguments", |
| FUNC_MAX_ARGS))); |
| |
| i = 0; |
| foreach(lc, argtypes) |
| { |
| TypeName *t = (TypeName *) lfirst(lc); |
| |
| argoids[i] = LookupTypeName(NULL, t); |
| if (!OidIsValid(argoids[i])) |
| ereport(ERROR, |
| (errcode(ERRCODE_UNDEFINED_OBJECT), |
| errmsg("type \"%s\" does not exist", |
| TypeNameToString(t)), |
| errOmitLocation(true))); |
| i++; |
| } |
| |
| oid = LookupFuncName(aggname, argcount, argoids, true); |
| |
| if (!OidIsValid(oid)) |
| { |
| if (noError) |
| return InvalidOid; |
| if (argcount == 0) |
| ereport(ERROR, |
| (errcode(ERRCODE_UNDEFINED_FUNCTION), |
| errmsg("aggregate %s(*) does not exist", |
| NameListToString(aggname)), |
| errOmitLocation(true))); |
| else |
| ereport(ERROR, |
| (errcode(ERRCODE_UNDEFINED_FUNCTION), |
| errmsg("aggregate %s does not exist", |
| func_signature_string(aggname, |
| argcount, argoids)), |
| errOmitLocation(true))); |
| } |
| |
| /* Make sure it's an aggregate */ |
| /* SELECT proisagg FROM pg_proc */ |
| |
| pcqCtx = caql_beginscan( |
| NULL, |
| cql("SELECT * FROM pg_proc " |
| " WHERE oid = :1 ", |
| ObjectIdGetDatum(oid))); |
| |
| ftup = caql_getnext(pcqCtx); |
| |
| if (!HeapTupleIsValid(ftup)) /* should not happen */ |
| elog(ERROR, "cache lookup failed for function %u", oid); |
| pform = (Form_pg_proc) GETSTRUCT(ftup); |
| |
| proisagg = pform->proisagg; |
| |
| caql_endscan(pcqCtx); |
| |
| if (!proisagg) |
| { |
| if (noError) |
| return InvalidOid; |
| /* we do not use the (*) notation for functions... */ |
| ereport(ERROR, |
| (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
| errmsg("function %s is not an aggregate", |
| func_signature_string(aggname, |
| argcount, argoids)))); |
| } |
| |
| return oid; |
| } |
| |
| |
| /* |
| * parseCheckTableFunctions |
| * |
| * Check for TableValueExpr where they shouldn't be. Currently the only |
| * valid location for a TableValueExpr is within a call to a table function. |
| * In the full SQL Standard they can exist anywhere a multiset is supported. |
| */ |
| void |
| parseCheckTableFunctions(ParseState *pstate, Query *qry) |
| { |
| check_table_func_context context; |
| context.parent = NULL; |
| query_tree_walker(qry, |
| checkTableFunctions_walker, |
| (void *) &context, 0); |
| } |
| |
| static bool |
| checkTableFunctions_walker(Node *node, check_table_func_context *context) |
| { |
| if (node == NULL) |
| return false; |
| |
| /* |
| * TABLE() value expressions are currently only permited as parameters |
| * to table functions called in the FROM clause. |
| */ |
| if (IsA(node, TableValueExpr)) |
| { |
| if (context->parent && IsA(context->parent, FuncExpr)) |
| { |
| FuncExpr *parent = (FuncExpr *) context->parent; |
| |
| /* |
| * This flag is set in addRangeTableEntryForFunction for functions |
| * called as range table entries having TABLE value expressions |
| * as arguments. |
| */ |
| if (parent->is_tablefunc) |
| return false; |
| |
| /* Error message could be improved */ |
| ereport(ERROR, |
| (errcode(ERRCODE_SYNTAX_ERROR), |
| errmsg("table functions must be invoked in FROM clause"))); |
| } |
| ereport(ERROR, |
| (errcode(ERRCODE_SYNTAX_ERROR), |
| errmsg("invalid use of TABLE value expression"))); |
| return true; /* not possible, but keeps compiler happy */ |
| } |
| |
| context->parent = node; |
| if (IsA(node, Query)) |
| { |
| return query_tree_walker((Query *) node, |
| checkTableFunctions_walker, |
| (void *) context, 0); |
| } |
| else |
| { |
| return expression_tree_walker(node, |
| checkTableFunctions_walker, |
| (void *) context); |
| } |
| } |