openGauss-server/src/common/backend/parser/parse_func.cpp

2339 lines
87 KiB
C++

/* -------------------------------------------------------------------------
*
* parse_func.c
* handle function calls in parser
*
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 2021, openGauss Contributors
*
*
* IDENTIFICATION
* src/backend/parser/parse_func.c
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include "catalog/gs_encrypted_proc.h"
#include "catalog/namespace.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/pg_list.h"
#include "parser/parse_agg.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.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/lsyscache.h"
#include "utils/syscache.h"
#include "catalog/gs_encrypted_columns.h"
static Oid FuncNameAsType(List* funcname);
static Node* ParseComplexProjection(ParseState* pstate, char* funcname, Node* first_arg, int location);
static List* GetDefaultVale(Oid funcoid, const int* argnumbers, int ndargs);
static Oid cl_get_input_param_original_type(Oid func_oid, int argno);
/*
* 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.
* Also, when is_column is true, we return NULL on failure rather than
* reporting a no-such-function error.
*
* The argument expressions (in fargs) must have been transformed already.
* But the agg_order expressions, if any, have not been.
*/
Node* ParseFuncOrColumn(ParseState* pstate, List* funcname, List* fargs, FuncCall* fn, int location, bool call_func)
{
bool is_column = (fn == NULL);
List* agg_order = (fn ? fn->agg_order : NIL);
bool agg_within_group = (fn ? fn->agg_within_group : false);
bool agg_star = (fn ? fn->agg_star : false);
bool agg_distinct = (fn ? fn->agg_distinct : false);
bool func_variadic = (fn ? fn->func_variadic : false);
WindowDef* over = (fn ? fn->over : NULL);
Oid rettype;
int rettype_orig = -1;
Oid funcid;
ListCell* l = NULL;
ListCell* nextl = NULL;
Node* first_arg = NULL;
int nargs;
int nargsplusdefs;
Oid actual_arg_types[FUNC_MAX_ARGS];
Oid* declared_arg_types = NULL;
List* argnames = NIL;
List* argdefaults = NIL;
Node* retval = NULL;
bool retset = false;
int nvargs;
Oid vatype;
FuncDetailCode fdresult;
char aggkind = 'n';
char* name_string = NULL;
Oid refSynOid = InvalidOid;
/*
* 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 = (Node*)lfirst(l);
Oid argtype = exprType(arg);
nextl = lnext(l);
if (argtype == VOIDOID && IsA(arg, Param) && !is_column && !agg_within_group) {
fargs = list_delete_ptr(fargs, arg);
continue;
}
actual_arg_types[nargs++] = argtype;
}
/*
* Check for named arguments; if there are any, build a list of names.
*
* We allow mixed notation (some named and some not), but only with all
* the named parameters after all the unnamed ones. So the name list
* corresponds to the last N actual parameters and we don't need any extra
* bookkeeping to match things up.
*/
argnames = NIL;
foreach (l, fargs) {
Node* arg = (Node*)lfirst(l);
if (IsA(arg, NamedArgExpr)) {
NamedArgExpr* na = (NamedArgExpr*)arg;
ListCell* lc = NULL;
/* Reject duplicate arg names */
foreach (lc, argnames) {
if (strcmp(na->name, (char*)lfirst(lc)) == 0)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("argument name \"%s\" used more than once", na->name),
parser_errposition(pstate, na->location)));
}
argnames = lappend(argnames, na->name);
} else {
if (argnames != NIL)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("positional argument cannot follow named argument"),
parser_errposition(pstate, exprLocation(arg))));
}
}
if (fargs != NIL) {
first_arg = (Node*)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, nor an argument name.
*/
if (nargs == 1 && agg_order == NIL && !agg_star && !agg_distinct && over == NULL && !func_variadic &&
argnames == NIL && 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 != NULL)
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.
*
* Note: for a named-notation or variadic function call, the reported
* "true" types aren't really what is in pg_proc: the types are reordered
* to match the given argument order of named arguments, and a variadic
* argument is replaced by a suitable number of copies of its element
* type. We'll fix up the variadic case below. We may also have to deal
* with default arguments.
*/
fdresult = func_get_detail(funcname,
fargs,
argnames,
nargs,
actual_arg_types,
!func_variadic,
true,
&funcid,
&rettype,
&retset,
&nvargs,
&vatype,
&declared_arg_types,
&argdefaults,
call_func,
&refSynOid,
&rettype_orig);
name_string = NameListToString(funcname);
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,
(Node*)linitial(fargs),
actual_arg_types[0],
rettype,
rettype_orig,
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", name_string, name_string),
parser_errposition(pstate, location)));
if (agg_distinct)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("DISTINCT specified, but %s is not an aggregate function", name_string),
parser_errposition(pstate, location)));
if (agg_within_group)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("WITHIN GROUP specified, but %s is not an aggregate function", name_string),
parser_errposition(pstate, location)));
if (agg_order != NIL)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("ORDER BY specified, but %s is not an aggregate function", name_string),
parser_errposition(pstate, location)));
if (over != NULL)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("OVER specified, but %s is not a window function nor an aggregate function", name_string),
parser_errposition(pstate, location)));
} else if (fdresult == FUNCDETAIL_AGGREGATE) {
/* It's an aggregate; fetch needed info from the pg_aggregate entry. */
HeapTuple tup;
int catDirectArgs = 0;
int pronargs = nargs;
bool isOrderedSet = false;
bool isnull = false;
tup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for aggregate %u", funcid);
/* 91269 version support orderedset agg */
aggkind = DatumGetChar(SysCacheGetAttr(AGGFNOID, tup, Anum_pg_aggregate_aggkind, &isnull));
if (!AGGKIND_IS_ORDERED_SET(aggkind))
aggkind = 'n';
isOrderedSet = AGGKIND_IS_ORDERED_SET(aggkind);
catDirectArgs = DatumGetInt8(SysCacheGetAttr(AGGFNOID, tup, Anum_pg_aggregate_aggnumdirectargs, &isnull));
ReleaseSysCache(tup);
/* Now do the saftey check. */
if (isOrderedSet) {
int numDirectArgs = 0;
if (!agg_within_group)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("WITHIN GROUP is required for ordered-set aggregate %s", name_string),
parser_errposition(pstate, location)));
if (over != NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("OVER is not supported for ordered-set aggregate %s", name_string),
parser_errposition(pstate, location)));
if (agg_distinct)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DISTINCT + WITHIN GROUP is not supported for ordered-set aggregate %s", name_string),
parser_errposition(pstate, location)));
if (func_variadic)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("VARIADIC + WITHIN GROUP is not supported for ordered-set aggregate %s", name_string),
parser_errposition(pstate, location)));
/*
* func_get_detail might have selected an aggregate that doesn't
* really match because it requires a different division of direct
* and aggregated arguments. Check if the number of direct arguments
* is actually equal to the number record in the pp_aggregate table.
* While agg_order is the number of aggregated args, we can conduct
* the number of direct args.
*/
numDirectArgs = nargs - list_length(agg_order);
Assert(numDirectArgs >= 0);
if (nvargs > 1)
pronargs -= nvargs - 1;
/* If it isn't variadic or the agg was "... ORDER BY VARIADIC" */
if (!OidIsValid(vatype) || catDirectArgs < pronargs) {
if (numDirectArgs != catDirectArgs)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(funcname, nargs, argnames, actual_arg_types)),
errhint("Ordered-set aggregate %s requires %d direct arguments, not %d.",
name_string,
catDirectArgs,
numDirectArgs),
parser_errposition(pstate, location)));
} else {
/* the agg was "..., VARIADIC ORDER BY VARIADIC" */
if (nvargs <= list_length(agg_order))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(funcname, nargs, argnames, actual_arg_types)),
errhint("Ordered-set aggregate %s requires at least %d direct arguments.",
name_string,
catDirectArgs),
parser_errposition(pstate, location)));
}
} else {
/* Normal aggregate, so it can't have WITHIN GROUP */
if (agg_within_group)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("%s is not an ordered-set aggregate, so it cannot have WITHIN GROUP", name_string),
parser_errposition(pstate, location)));
}
} else if (fdresult == FUNCDETAIL_WINDOWFUNC) {
/*
* A true window function should be called with a window definition,
* which is assigned by over().
*/
if (over == NULL)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("window function %s requires an OVER clause", name_string),
parser_errposition(pstate, location)));
/* And, per spec, WITHIN GROUP isn't allowed */
if (agg_within_group)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("window function %s cannot have WITHIN GROUP", name_string),
parser_errposition(pstate, location)));
} else {
/*
* Oops. Time to die.
*
* If we are dealing with the attribute notation rel.function, let the
* caller handle failure.
*/
if (is_column)
return NULL;
/*
* 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, argnames, actual_arg_types)),
errhint("Could not choose a best candidate function. "
"You might need to add explicit type casts."),
parser_errposition(pstate, location)));
else if (list_length(agg_order) > 1 && !agg_within_group) {
/* It's agg(x, ORDER BY y,z) ... perhaps misplaced ORDER BY */
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(funcname, nargs, argnames, actual_arg_types)),
errhint("No aggregate function matches the given name and argument types. "
"Perhaps you misplaced ORDER BY; ORDER BY must appear "
"after all regular arguments of the aggregate."),
parser_errposition(pstate, location)));
} else {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(funcname, nargs, argnames, 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);
}
/*
* enforce consistency with polymorphic 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, false);
/* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types);
/*
* If it's a variadic function call, transform the last nvargs arguments
* into an array --- unless it's an "any" variadic.
*/
if (nvargs > 0 && declared_arg_types[nargs - 1] != ANYOID) {
ArrayExpr* newa = makeNode(ArrayExpr);
int non_var_args = nargs - nvargs;
List* vargs = NIL;
Assert(non_var_args >= 0);
vargs = list_copy_tail(fargs, non_var_args);
fargs = list_truncate(fargs, non_var_args);
newa->elements = vargs;
/* assume all the variadic arguments were coerced to the same type */
newa->element_typeid = exprType((Node*)linitial(vargs));
newa->array_typeid = get_array_type(newa->element_typeid);
if (!OidIsValid(newa->array_typeid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s", format_type_be(newa->element_typeid)),
parser_errposition(pstate, exprLocation((Node*)vargs))));
/* array_collid will be set by parse_collate.c */
newa->multidims = false;
newa->location = exprLocation((Node*)vargs);
fargs = lappend(fargs, newa);
}
/* build the appropriate output structure */
if (fdresult == FUNCDETAIL_NORMAL) {
FuncExpr* funcexpr = makeNode(FuncExpr);
funcexpr->funcid = funcid;
funcexpr->funcresulttype = rettype;
funcexpr->funcresulttype_orig = rettype_orig;
funcexpr->funcretset = retset;
funcexpr->funcvariadic = func_variadic;
funcexpr->funcformat = COERCE_EXPLICIT_CALL;
/* funccollid and inputcollid will be set by parse_collate.c */
funcexpr->args = fargs;
funcexpr->location = location;
/* refSynOid will be set when need to record dependency */
funcexpr->refSynOid = refSynOid;
retval = (Node*)funcexpr;
/*Return type of to_date function will be changed from timestamp to date type in C_FORMAT*/
if (u_sess->attr.attr_sql.sql_compatibility != A_FORMAT &&
(funcid == TODATEFUNCOID || funcid == TODATEDEFAULTFUNCOID)) {
FuncExpr* timestamp_date_fun = makeNode(FuncExpr);
timestamp_date_fun->funcid = TIMESTAMP2DATEOID;
timestamp_date_fun->funcresulttype = DATEOID;
timestamp_date_fun->funcretset = false;
timestamp_date_fun->funcformat = COERCE_EXPLICIT_CAST;
timestamp_date_fun->args = list_make1(funcexpr);
timestamp_date_fun->location = location;
retval = (Node*)timestamp_date_fun;
}
} else if (fdresult == FUNCDETAIL_AGGREGATE && over == NULL) {
/* aggregate function */
Aggref* aggref = makeNode(Aggref);
aggref->aggfnoid = funcid;
aggref->aggtype = rettype;
/* aggcollid and inputcollid will be set by parse_collate.c */
/* args, aggorder, aggdistinct will be set by transformAggregateCall */
aggref->aggstar = agg_star;
aggref->aggvariadic = func_variadic;
aggref->aggkind = aggkind;
/* agglevelsup will be set by transformAggregateCall */
aggref->location = location;
/*
* Reject attempt to call a parameterless aggregate without (*)
* syntax. This is mere pedantry but some folks insisted ...
*/
if (fargs == NIL && !agg_star && !agg_within_group)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("%s(*) must be used to call a parameterless aggregate function", NameListToString(funcname)),
parser_errposition(pstate, location)));
if (retset)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("aggregates cannot return sets"),
parser_errposition(pstate, location)));
/*
* Currently it's not possible to define an aggregate with named
* arguments, so this case should be impossible. Check anyway because
* the planner and executor wouldn't cope with NamedArgExprs in an
* Aggref node.
*/
if (argnames != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("aggregates cannot use named arguments"),
parser_errposition(pstate, location)));
/* parse_agg.c does additional aggregate-specific processing */
transformAggregateCall(pstate, aggref, fargs, agg_order, agg_distinct);
retval = (Node*)aggref;
} else {
/* window function */
WindowFunc* wfunc = makeNode(WindowFunc);
/*
* True window functions must be called with a window definition.
*/
if (over == NULL)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("window function call requires an OVER clause"),
parser_errposition(pstate, location)));
Assert(!agg_within_group);
wfunc->winfnoid = funcid;
wfunc->wintype = rettype;
/* wincollid and inputcollid will be set by parse_collate.c */
wfunc->args = fargs;
/* winref will be set by transformWindowFuncCall */
wfunc->winstar = agg_star;
wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE);
wfunc->location = location;
/*
* agg_star is allowed for aggregate functions but distinct isn't
*/
if (agg_distinct)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DISTINCT is not implemented for window functions"),
parser_errposition(pstate, location)));
/*
* Reject attempt to call a parameterless aggregate without (*)
* syntax. This is mere pedantry but some folks insisted ...
*/
if (wfunc->winagg && 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)));
/*
* ordered aggs not allowed in windows yet, execpt listagg
*/
if (pg_strcasecmp(NameListToString(funcname), "listagg") != 0 && agg_order != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("aggregate ORDER BY is not implemented for window functions"),
parser_errposition(pstate, location)));
if (retset)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("window functions cannot return sets"),
parser_errposition(pstate, location)));
/*
* We might want to support this later, but for now reject it because
* the planner and executor wouldn't cope with NamedArgExprs in a
* WindowFunc node.
*/
if (argnames != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("window functions cannot use named arguments"),
parser_errposition(pstate, location)));
/* For listagg, multiple different order info is not allowed.*/
ListCell* lc = NULL;
bool isInvalidOrder = false;
foreach (lc, agg_order) {
SortBy* aggorder = (SortBy*)lfirst(lc);
if (over->orderClause != NIL && !list_member(over->orderClause, aggorder)) {
isInvalidOrder = true;
break;
}
}
if (isInvalidOrder)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("window functions cannot allow multiple different order info"),
parser_errposition(pstate, location)));
if (over->orderClause == NIL)
over->orderClause = agg_order;
/* parse_agg.c does additional window-func-specific processing */
transformWindowFuncCall(pstate, wfunc, over);
retval = (Node*)wfunc;
}
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() */
/* get category priority
*
* for a given categoryoid of any
* 'X': unknown
* 'U': User
* 'B' Boolean
* 'G' Geometric
* 'I' Network
* the category priority should be 0
*/
static int GetCategoryPriority(TYPCATEGORY categoryoid)
{
int result = 0;
if (u_sess->attr.attr_sql.convert_string_to_digit) {
switch (categoryoid) {
case ('N'): /*Numeric*/
result = 4;
break;
case ('T'): /*Timespan*/
result = 3;
break;
case ('D'): /*Datetime*/
result = 2;
break;
case ('S'): /*String*/
result = 1;
break;
default:
result = 0;
break;
}
} else {
switch (categoryoid) {
case ('D'): /*Datetime*/
result = 1;
break;
case ('T'): /*Timespan*/
result = 2;
break;
case ('N'): /*Numeric*/
result = 3;
break;
case ('S'): /*String*/
result = 4;
break;
default:
result = 0;
break;
}
}
return result;
}
/* get type priority */
int GetPriority(Oid typeoid)
{
int result = 0;
switch (typeoid) {
/* bool */
case (BOOLOID):
result = 0;
break;
/* string */
case (CHAROID):
result = 0;
break;
case (NAMEOID):
result = 1;
break;
case (BPCHAROID):
result = 2;
break;
case (VARCHAROID):
case (NVARCHAR2OID):
result = 3;
break;
case (TEXTOID):
result = 4;
break;
/* bitstring */
case (BITOID):
result = 0;
break;
case (VARBITOID):
result = 1;
break;
/* numeric */
case (CASHOID):
result = 0;
break;
case (INT2OID):
result = 1;
break;
case (INT4OID):
result = 2;
break;
case (OIDOID):
result = 3;
break;
case (INT8OID):
result = 4;
break;
case (FLOAT4OID):
result = 5;
break;
case (FLOAT8OID):
result = 6;
break;
case (NUMERICOID):
result = 7;
break;
/* datetime */
case (TIMEOID):
result = 0;
break;
case (TIMETZOID):
result = 1;
break;
case (ABSTIMEOID):
result = 2;
break;
case (DATEOID):
result = 3;
break;
/*
* According to precision, priority of smalldatetime between date and timestamp.
* Smalldatetime will return 0 before that is very error.
*/
case (SMALLDATETIMEOID):
result = 4;
break;
case (TIMESTAMPOID):
result = 5;
break;
case (TIMESTAMPTZOID):
result = 6;
break;
/* timespan */
case (RELTIMEOID):
result = 0;
break;
case (TINTERVALOID):
result = 1;
break;
case (INTERVALOID):
result = 2;
break;
/* the types below are not used by now */
case (POINTOID):
case (LSEGOID):
case (PATHOID):
case (BOXOID):
case (POLYGONOID):
case (LINEOID):
case (CIRCLEOID):
result = 0;
break;
case (INETOID):
case (CIDROID):
result = 0;
break;
case (UNKNOWNOID):
case (InvalidOid):
result = 0;
break;
/* OID */
case (REGPROCOID):
case (REGPROCEDUREOID):
case (REGOPEROID):
case (REGOPERATOROID):
case (REGCLASSOID):
case (REGTYPEOID):
case (REGCONFIGOID):
case (REGDICTIONARYOID):
result = 0;
break;
case (RECORDOID):
case (CSTRINGOID):
case (ANYOID):
case (ANYARRAYOID):
case (VOIDOID):
case (TRIGGEROID):
case (LANGUAGE_HANDLEROID):
case (INTERNALOID):
case (OPAQUEOID):
case (ANYELEMENTOID):
case (ANYNONARRAYOID):
case (ANYENUMOID):
result = 0;
break;
default:
result = 0;
break;
}
return result;
}
/* get type with highest priority within specific category */
static Oid get_highest_type(TYPCATEGORY category)
{
Oid result = InvalidOid;
switch (category) {
case 'N':
result = NUMERICOID;
break;
case 'S':
result = TEXTOID;
break;
case 'V':
result = VARBITOID;
break;
case 'D':
/* let it be the original type */
break;
case 'T':
result = INTERVALOID;
break;
default:
break;
}
return result;
}
/* @Description: keep the candidate into the list.
* @in nmatch: number of matche found already.
* @inout nbestMatch: number of best match found already
* @in current_candidate: current candidate to be put in.
* @out last_candidate: last candidate in the list.
* @out candidates: first candidate in the list.
* @out ncandidates: number of candidates in the list.
*/
static void keep_candidate(int nmatch, int& nbestMatch, FuncCandidateList current_candidate,
FuncCandidateList& last_candidate, FuncCandidateList& candidates, int& ncandidates)
{
if ((nmatch > nbestMatch) || (last_candidate == NULL)) {
/* take this one as the best choice so far? */
nbestMatch = nmatch;
candidates = current_candidate;
last_candidate = current_candidate;
ncandidates = 1;
} else if (nmatch == nbestMatch) {
/* no worse than the last choice, so keep this one too? */
last_candidate->next = current_candidate;
last_candidate = current_candidate;
ncandidates++;
}
/* otherwise, don't bother keeping this one... */
}
/* 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, first_candidate, last_candidate;
Oid* current_typeids = NULL;
Oid current_type;
int i;
int ncandidates;
int nbestMatch, nmatch, nunknowns;
Oid input_base_typeids[FUNC_MAX_ARGS];
TYPCATEGORY slot_category[FUNC_MAX_ARGS], current_category = TYPCATEGORY_INVALID;
bool current_is_preferred = false;
bool slot_has_preferred_type[FUNC_MAX_ARGS];
bool resolved_unknowns = false;
int type_priority[FUNC_MAX_ARGS];
bool different_category = false;
/* protect local fixed-size arrays */
if (nargs > 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)));
/*
* 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.
*
* While we're at it, count the number of unknown-type arguments for use
* later.
*/
nunknowns = 0;
for (i = 0; i < nargs; i++) {
if (input_typeids[i] != UNKNOWNOID)
input_base_typeids[i] = getBaseType(input_typeids[i]);
else {
/* no need to call getBaseType on UNKNOWNOID */
input_base_typeids[i] = UNKNOWNOID;
nunknowns++;
}
}
/*
* Run through all candidates and keep those with the most matches on
* exact types. Keep all candidates if none match.
*/
for (i = 0; i < nargs; i++) /* avoid multiple lookups */
{
slot_category[i] = TypeCategory(input_base_typeids[i]);
/*
* For C, we should choose numeric + numeric for varchar + int,
* so we should also admit highest type conversion for operations
* between different type categories
*/
if (u_sess->attr.attr_sql.sql_compatibility == C_FORMAT && !different_category &&
slot_category[i] != TYPCATEGORY_UNKNOWN) {
if (current_category == TYPCATEGORY_INVALID)
current_category = slot_category[i];
else if (slot_category[i] != current_category)
different_category = true;
}
}
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] ||
(different_category && current_typeids[i] == get_highest_type(slot_category[i]))))
nmatch++;
}
keep_candidate(nmatch, nbestMatch, current_candidate, last_candidate, candidates, ncandidates);
}
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.
*/
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]) ||
(different_category && current_typeids[i] == get_highest_type(slot_category[i])))
nmatch++;
}
}
keep_candidate(nmatch, nbestMatch, current_candidate, last_candidate, candidates, 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 inputs.
*
* If there are no unknown inputs, we have no more heuristics that apply,
* and must fail.
*/
// Add the following codes dealing with candidates:
// 1) Try to use the priority,for example , in numeric type family,we have the priority
// from low to high like int2->int4->int8->numeric->float4->float8
// 2) Still too many candidates,try to use category priority
for (i = 0; i < nargs; i++)
type_priority[i] = GetPriority(input_base_typeids[i]); /* get priority in type family */
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] ||
(slot_category[i] == TypeCategory(current_typeids[i]) &&
GetPriority(current_typeids[i]) > type_priority[i]))
nmatch++;
}
}
keep_candidate(nmatch, nbestMatch, current_candidate, last_candidate, candidates, ncandidates);
}
if (last_candidate) /* terminate rebuilt list */
last_candidate->next = NULL;
if (ncandidates == 1)
return candidates;
/*
* The next step examines 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 just one candidate remains, return
* that one. However, if this rule turns out to reject all candidates,
* keep them all instead.
*/
resolved_unknowns = false;
for (i = 0; i < nargs; i++) {
bool have_conflict = false;
if (input_base_typeids[i] != UNKNOWNOID)
continue;
resolved_unknowns = true; /* assume we can do it */
slot_category[i] = TYPCATEGORY_INVALID;
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];
get_type_category_preferred(current_type, &current_category, &current_is_preferred);
if (slot_category[i] == TYPCATEGORY_INVALID) {
/* first candidate */
slot_category[i] = current_category;
slot_has_preferred_type[i] = current_is_preferred;
} else if (current_category == slot_category[i]) {
/* more candidates in same category */
slot_has_preferred_type[i] = slot_has_preferred_type[i] || current_is_preferred;
} else {
/* category conflict! */
if (current_category == TYPCATEGORY_STRING) {
/* STRING always wins if available */
slot_category[i] = current_category;
slot_has_preferred_type[i] = current_is_preferred;
} else {
/*
* Remember conflict, but keep going (might find STRING)
*/
have_conflict = true;
}
}
}
if (have_conflict && slot_category[i] != TYPCATEGORY_STRING) {
/* Failed to resolve category conflict at this position */
resolved_unknowns = false;
break;
}
}
if (resolved_unknowns) {
/* Strip non-matching candidates */
ncandidates = 0;
first_candidate = candidates;
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];
get_type_category_preferred(current_type, &current_category, &current_is_preferred);
if (current_category != slot_category[i]) {
keepit = false;
break;
}
if (slot_has_preferred_type[i] && !current_is_preferred) {
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
first_candidate = current_candidate->next;
}
}
/* if we found any matches, restrict our attention to those */
if (last_candidate) {
candidates = first_candidate;
/* terminate rebuilt list */
last_candidate->next = NULL;
}
if (ncandidates == 1)
return candidates;
}
/*
* Last gasp: if there are both known- and unknown-type inputs, and all
* the known types are the same, assume the unknown inputs are also that
* type, and see if that gives us a unique match. If so, use that match.
*
* NOTE: for a binary operator with one unknown and one non-unknown input,
* we already tried this heuristic in binary_oper_exact(). However, that
* code only finds exact matches, whereas here we will handle matches that
* involve coercion, polymorphic type resolution, etc.
*/
if (nunknowns < nargs) {
Oid known_type = UNKNOWNOID;
for (i = 0; i < nargs; i++) {
if (input_base_typeids[i] == UNKNOWNOID)
continue;
if (known_type == UNKNOWNOID) /* first known arg? */
known_type = input_base_typeids[i];
else if (known_type != input_base_typeids[i]) {
/* oops, not all match */
known_type = UNKNOWNOID;
break;
}
}
if (known_type != UNKNOWNOID) {
/* okay, just one known type, apply the heuristic */
for (i = 0; i < nargs; i++)
input_base_typeids[i] = known_type;
ncandidates = 0;
last_candidate = NULL;
for (current_candidate = candidates; current_candidate != NULL;
current_candidate = current_candidate->next) {
current_typeids = current_candidate->args;
if (can_coerce_type(nargs, input_base_typeids, current_typeids, COERCION_IMPLICIT)) {
if (++ncandidates > 1)
break; /* not unique, give up */
last_candidate = current_candidate;
}
}
if (ncandidates == 1) {
/* successfully identified a unique match */
last_candidate->next = NULL;
return last_candidate;
}
}
}
// still too many candidates, try to use category priority
if (ncandidates > 1) {
TYPCATEGORY maxCatalog[FUNC_MAX_ARGS];
Oid maxTyp[FUNC_MAX_ARGS];
last_candidate = NULL;
ncandidates = 0;
nbestMatch = 0;
for (i = 0; i < nargs; i++) {
input_base_typeids[i] = getBaseType(input_typeids[i]);
slot_category[i] = TypeCategory(input_base_typeids[i]);
maxCatalog[i] = 'X';
maxTyp[i] = UNKNOWNOID;
}
/* Find out in type category which has the highest priority*/
for (current_candidate = candidates; current_candidate != NULL; current_candidate = current_candidate->next) {
current_typeids = current_candidate->args;
for (i = 0; i < nargs; i++) {
if (GetCategoryPriority(TypeCategory(current_typeids[i])) > GetCategoryPriority(maxCatalog[i])) {
maxCatalog[i] = TypeCategory(current_typeids[i]);
maxTyp[i] = current_typeids[i];
} else if ((TypeCategory(current_typeids[i]) == maxCatalog[i]) &&
(GetPriority(maxTyp[i]) < GetPriority(current_typeids[i]))) {
maxTyp[i] = current_typeids[i];
}
}
}
/* If the input parameter's priority is higher than the biggest priority, the biggest priority shall prevail*/
for (i = 0; i < nargs; i++) {
if ((GetCategoryPriority(slot_category[i]) > GetCategoryPriority(maxCatalog[i])) ||
(slot_category[i] == 'X')) {
slot_category[i] = maxCatalog[i];
input_base_typeids[i] = maxTyp[i];
} else if ((slot_category[i] == maxCatalog[i]) &&
(GetPriority(input_base_typeids[i]) > GetPriority(maxTyp[i]))) {
input_base_typeids[i] = maxTyp[i];
} else {
slot_category[i] = maxCatalog[i];
input_base_typeids[i] = maxTyp[i];
}
type_priority[i] = GetPriority(input_base_typeids[i]);
}
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 the type has higher priority is chosen */
if (GetCategoryPriority(slot_category[i]) < GetCategoryPriority(TypeCategory(current_typeids[i]))) {
nmatch++;
} else if (GetCategoryPriority(slot_category[i]) ==
GetCategoryPriority(TypeCategory(current_typeids[i]))) {
if (type_priority[i] <= GetPriority(current_typeids[i])) {
nmatch++;
}
}
}
keep_candidate(nmatch, nbestMatch, current_candidate, last_candidate, candidates, ncandidates);
}
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() */
/*
* sort_candidate_func_list
*
* sort the candidate functions by function's all param num.
*/
FuncCandidateList sort_candidate_func_list(FuncCandidateList oldCandidates)
{
if (oldCandidates == NULL || oldCandidates->next == NULL) {
return oldCandidates;
}
FuncCandidateList cur = oldCandidates;
int size = 0;
while (cur) {
size++;
cur = cur->next;
}
cur = oldCandidates;
FuncCandidateList* candidates = (FuncCandidateList*)palloc0(sizeof(FuncCandidateList) * size);
int index = 0;
while (cur) {
candidates[index++] = cur;
cur = cur->next;
}
FuncCandidateList sortedCandidates = NULL;
FuncCandidateList lastCandidate = NULL;
for (int i = 0; i < size; i++) {
if (candidates[i] == NULL) {
continue;
}
int smallestIndex = i;
for (int j = 0; j < size; j++) {
FuncCandidateList cur2 = candidates[j];
if (cur2 != NULL && candidates[smallestIndex]->allArgNum > cur2->allArgNum) {
smallestIndex = j;
}
}
FuncCandidateList smallest = candidates[smallestIndex];
if (lastCandidate == NULL) {
lastCandidate = smallest;
sortedCandidates = smallest;
} else {
lastCandidate->next = smallest;
lastCandidate = lastCandidate->next;
smallest->next = NULL;
}
candidates[smallestIndex] = NULL;
}
pfree(candidates);
return sortedCandidates;
}
/* 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 type coercion request
* 2) apply the ambiguous-function resolution rules
*
* Return values *funcid through *true_typeids receive info about the function.
* If argdefaults isn't NULL, *argdefaults receives a list of any default
* argument expressions that need to be added to the given arguments.
*
* When processing a named- or mixed-notation call (ie, fargnames isn't NIL),
* the returned true_typeids and argdefaults are ordered according to the
* call's argument ordering: first any positional arguments, then the named
* arguments, then defaulted arguments (if needed and allowed by
* expand_defaults). Some care is needed if this information is to be compared
* to the function's pg_proc entry, but in practice the caller can usually
* just work with the call's argument ordering.
*
* We rely primarily on fargnames/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 need that check made. Note also that when fargnames isn't NIL,
* the fargs list must be passed if the caller wants actual argument position
* information to be returned into the NamedArgExpr nodes.
*/
FuncDetailCode func_get_detail(List* funcname, List* fargs, List* fargnames, int nargs, Oid* argtypes,
bool expand_variadic, bool expand_defaults, Oid* funcid, /* return value */
Oid* rettype, /* return value */
bool* retset, /* return value */
int* nvargs, /* return value */
Oid* vatype, /* return value */
Oid** true_typeids, /* return value */
List** argdefaults, bool call_func, /* optional return value */
Oid* refSynOid, int* rettype_orig)
{
FuncCandidateList raw_candidates = NULL;
FuncCandidateList all_candidates = NULL;
FuncCandidateList best_candidate;
/* initialize output arguments to silence compiler warnings */
*funcid = InvalidOid;
*rettype = InvalidOid;
*retset = false;
*nvargs = 0;
*true_typeids = NULL;
if (argdefaults != NULL)
*argdefaults = NIL;
if (refSynOid != NULL) {
*refSynOid = InvalidOid;
}
#ifndef ENABLE_MULTIPLE_NODES
if (enable_out_param_override()) {
/* For A compatiablity, CALL statement only can invoke Procedure in SQL or Function in PLSQL, */
/* and SELECT statement can only invoke Function. but now it does't distinguish for compatible with the old code.*/
raw_candidates = FuncnameGetCandidates(funcname, nargs, fargnames, expand_variadic, expand_defaults, false, true, PROKIND_UNKNOWN);
} else {
/* Get list of possible candidates from namespace search */
raw_candidates = FuncnameGetCandidates(funcname, nargs, fargnames, expand_variadic, expand_defaults, false);
/* Get list of possible candidates from namespace search including proallargtypes for package function */
if (call_func && IsPackageFunction(funcname)) {
all_candidates =
FuncnameGetCandidates(funcname, nargs, fargnames, expand_variadic, expand_defaults, false, true);
if (all_candidates != NULL) {
if (raw_candidates != NULL) {
best_candidate = raw_candidates;
while (best_candidate && best_candidate->next) {
best_candidate = best_candidate->next;
}
best_candidate->next = all_candidates;
} else {
raw_candidates = all_candidates;
}
}
}
}
raw_candidates = sort_candidate_func_list(raw_candidates);
#else
/* Get list of possible candidates from namespace search */
raw_candidates = FuncnameGetCandidates(funcname, nargs, fargnames, expand_variadic, expand_defaults, false);
/* Get list of possible candidates from namespace search including proallargtypes for package function */
if (call_func && IsPackageFunction(funcname)) {
all_candidates =
FuncnameGetCandidates(funcname, nargs, fargnames, expand_variadic, expand_defaults, false, true);
if (all_candidates != NULL) {
if (raw_candidates != NULL) {
best_candidate = raw_candidates;
while (best_candidate && best_candidate->next) {
best_candidate = best_candidate->next;
}
best_candidate->next = all_candidates;
} else {
raw_candidates = all_candidates;
}
}
}
#endif
/*
* 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 the coercion path is RELABELTYPE or COERCEVIAIO, 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 simple coercion, not as
* "text(name(varchar))" which the code below this point is entirely
* capable of selecting.
*
* We also treat a coercion of a previously-unknown-type literal
* constant to a specific type this way.
*
* The reason we reject COERCION_PATH_FUNC here is that we expect the
* cast implementation function to be named after the target type.
* Thus the function will be found by normal lookup if appropriate.
*
* The reason we reject COERCION_PATH_ARRAYCOERCE is mainly that you
* can't write "foo[] (something)" as a function call. In theory
* someone might want to invoke it as "_foo (something)" but we have
* never supported that historically, so we can insist that people
* write it as a normal cast instead.
*
* We also reject the specific case of COERCEVIAIO for a composite
* source type and a string-category target type. This is a case that
* find_coercion_pathway() allows by default, but experience has shown
* that it's too commonly invoked by mistake. So, again, insist that
* people use cast syntax if they want to do that.
*
* NB: it's important that this code does not exceed 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 && fargnames == NIL) {
Oid targetType = FuncNameAsType(funcname);
if (OidIsValid(targetType)) {
Oid sourceType = argtypes[0];
Node* arg1 = (Node*)linitial(fargs);
bool iscoercion = false;
if (sourceType == UNKNOWNOID && IsA(arg1, Const)) {
/* always treat typename('literal') as coercion */
iscoercion = true;
} else {
CoercionPathType cpathtype;
Oid cfuncid;
cpathtype = find_coercion_pathway(targetType, sourceType, COERCION_EXPLICIT, &cfuncid);
switch (cpathtype) {
case COERCION_PATH_RELABELTYPE:
iscoercion = true;
break;
case COERCION_PATH_COERCEVIAIO:
if ((sourceType == RECORDOID || ISCOMPLEX(sourceType)) &&
TypeCategory(targetType) == TYPCATEGORY_STRING)
iscoercion = false;
else
iscoercion = true;
break;
default:
iscoercion = false;
break;
}
}
if (iscoercion) {
/* Treat it as a type coercion */
*funcid = InvalidOid;
*rettype = targetType;
*retset = false;
*nvargs = 0;
*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, &current_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;
FuncDetailCode result;
/*
* If processing named args or expanding variadics or defaults, the
* "best candidate" might represent multiple equivalently good
* functions; treat this case as ambiguous.
*/
if (!OidIsValid(best_candidate->oid))
return FUNCDETAIL_MULTIPLE;
/*
* We disallow VARIADIC with named arguments unless the last argument
* (the one with VARIADIC attached) actually matched the variadic
* parameter. This is mere pedantry, really, but some folks insisted.
*/
if (fargnames != NIL && !expand_variadic && nargs > 0 && best_candidate->argnumbers != NULL &&
best_candidate->argnumbers[nargs - 1] != nargs - 1)
return FUNCDETAIL_NOTFOUND;
*funcid = best_candidate->oid;
*nvargs = best_candidate->nvargs;
*true_typeids = best_candidate->args;
/* Store OID of referenced synonym, if needed. */
if (refSynOid != NULL) {
*refSynOid = best_candidate->refSynOid;
}
/*
* If processing named args, return actual argument positions into
* NamedArgExpr nodes in the fargs list. This is a bit ugly but not
* worth the extra notation needed to do it differently.
*/
if (best_candidate->argnumbers != NULL) {
int i = 0;
ListCell* lc = NULL;
foreach (lc, fargs) {
NamedArgExpr* na = (NamedArgExpr*)lfirst(lc);
if (IsA(na, NamedArgExpr))
na->argnumber = best_candidate->argnumbers[i];
i++;
}
}
ftup = SearchSysCache1(PROCOID, ObjectIdGetDatum(best_candidate->oid));
if (!HeapTupleIsValid(ftup)) /* should not happen */
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED),
errmsg("cache lookup failed for function %u", best_candidate->oid)));
pform = (Form_pg_proc)GETSTRUCT(ftup);
*rettype = pform->prorettype;
if (IsClientLogicType(*rettype) && rettype_orig) {
HeapTuple gstup = SearchSysCache1(GSCLPROCID, ObjectIdGetDatum(best_candidate->oid));
if (!HeapTupleIsValid(gstup)) /* should not happen */
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
errmsg("cache lookup failed for function %u", best_candidate->oid)));
Form_gs_encrypted_proc gsform = (Form_gs_encrypted_proc)GETSTRUCT(gstup);
*rettype_orig = gsform->prorettype_orig;
ReleaseSysCache(gstup);
}
*retset = pform->proretset;
*vatype = pform->provariadic;
/* fetch default args if caller wants 'em */
if (argdefaults != NULL && best_candidate->ndargs > 0) {
/* shouldn't happen, FuncnameGetCandidates messed up */
if (best_candidate->ndargs > pform->pronargdefaults)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION), errmodule(MOD_OPT), errmsg("not enough default arguments")));
*argdefaults = GetDefaultVale(*funcid, best_candidate->argnumbers, best_candidate->ndargs);
}
if (pform->proisagg)
result = FUNCDETAIL_AGGREGATE;
else if (pform->proiswindow)
result = FUNCDETAIL_WINDOWFUNC;
else
result = FUNCDETAIL_NORMAL;
ReleaseSysCache(ftup);
return result;
}
return FUNCDETAIL_NOTFOUND;
}
/*
* 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 = NULL;
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]) {
Node* node = (Node*)lfirst(current_fargs);
/*
* If arg is a NamedArgExpr, coerce its input expr instead --- we
* want the NamedArgExpr to stay at the top level of the list.
*/
if (IsA(node, NamedArgExpr)) {
NamedArgExpr* na = (NamedArgExpr*)node;
node = coerce_type(pstate,
(Node*)na->arg,
actual_arg_types[i],
declared_arg_types[i],
-1,
COERCION_IMPLICIT,
COERCE_IMPLICIT_CAST,
-1);
na->arg = (Expr*)node;
} else {
node = coerce_type(pstate,
node,
actual_arg_types[i],
declared_arg_types[i],
-1,
COERCION_IMPLICIT,
COERCE_IMPLICIT_CAST,
-1);
lfirst(current_fargs) = node;
}
}
i++;
}
}
/*
* FuncNameAsType -
* convenience routine to see if a function name matches a type name
*
* Returns the OID of the matching type, or InvalidOid if none. We ignore
* shell types and complex types.
*/
static Oid FuncNameAsType(List* funcname)
{
Oid result;
Type typtup;
/*
* temp_ok=false protects the <refsect1 id="sql-createfunction-security">
* contract for writing SECURITY DEFINER functions safely.
*/
#ifdef ENABLE_MULTIPLE_NODES
typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL);
#else
typtup = LookupTypeNameExtended(NULL, makeTypeNameFromNameList(funcname), NULL, false);
#endif
if (typtup == NULL)
return InvalidOid;
if (((Form_pg_type)GETSTRUCT(typtup))->typisdefined && !OidIsValid(typeTypeRelid(typtup)))
result = typeTypeId(typtup);
else
result = InvalidOid;
ReleaseSysCache(typtup);
return result;
}
/*
* 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 = NULL;
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 */
AssertEreport(tupdesc, MOD_OPT, "");
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;
/* save attribute's collation for parse_collate.c */
fselect->resultcollid = att->attcollation;
return (Node*)fselect;
}
}
return NULL; /* funcname does not match any column */
}
/*
* funcname_signature_string
* Build a string representing a function name, including arg types.
* The result is something like "foo(integer)".
*
* If argnames isn't NIL, it is a list of C strings representing the actual
* arg names for the last N arguments. This must be considered part of the
* function signature too, when dealing with named-notation function calls.
*
* This is typically used in the construction of function-not-found error
* messages.
*/
const char* funcname_signature_string(const char* funcname, int nargs, List* argnames, const Oid* argtypes)
{
StringInfoData argbuf;
int numposargs;
ListCell* lc = NULL;
int i;
initStringInfo(&argbuf);
appendStringInfo(&argbuf, "%s(", funcname);
numposargs = nargs - list_length(argnames);
lc = list_head(argnames);
for (i = 0; i < nargs; i++) {
if (i)
appendStringInfoString(&argbuf, ", ");
if (i >= numposargs) {
appendStringInfo(&argbuf, "%s := ", (char*)lfirst(lc));
lc = lnext(lc);
}
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, List* argnames, const Oid* argtypes)
{
return funcname_signature_string(NameListToString(funcname), nargs, argnames, 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, NIL, false, false, false);
while (clist) {
/* if argtype is CL type replace it with original type */
for (int i = 0; i < nargs; i++) {
if (IsClientLogicType(clist->args[i])) {
clist->args[i] = cl_get_input_param_original_type(clist->oid, i);
}
}
if (memcmp(argtypes, clist->args, nargs * sizeof(Oid)) == 0 && OidIsValid(clist->oid))
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, NIL, argtypes))));
return InvalidOid;
}
/*
* LookupTypeNameOid
* Convenience routine to look up a type, silently accepting shell types
*/
Oid LookupTypeNameOid(const TypeName* typname)
{
Oid result;
Type typtup;
typtup = LookupTypeName(NULL, typname, NULL);
if (typtup == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type \"%s\" does not exist", TypeNameToString(typname))));
result = typeTypeId(typtup);
ReleaseSysCache(typtup);
return result;
}
/*
* 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 = NULL;
argcount = list_length(argtypes);
if (argcount > FUNC_MAX_ARGS)
ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg_plural("functions cannot have more than %d argument",
"functions cannot have more than %d arguments",
FUNC_MAX_ARGS,
FUNC_MAX_ARGS)));
args_item = list_head(argtypes);
for (i = 0; i < argcount; i++) {
TypeName* t = (TypeName*)lfirst(args_item);
argoids[i] = LookupTypeNameOid(t);
args_item = lnext(args_item);
}
return LookupFuncName(funcname, argcount, argoids, noError);
}
// Find the function by name and with optional argument
//
// This function is like LookupFuncNameTypeNames, used in
// drop function, when no argument specified, compatible
// with A db
Oid LookupFuncNameOptTypeNames(List* funcname, List* argtypes, bool noError)
{
FuncCandidateList cnddt_func_list = NULL;
if (argtypes != NIL)
return LookupFuncNameTypeNames(funcname, argtypes, noError);
/* If argument is not specified (compitable with A db)*/
cnddt_func_list = FuncnameGetCandidates(funcname, -1, NULL, false, false, false);
if (cnddt_func_list == NULL) {
if (!noError)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist", NameListToString(funcname))));
return InvalidOid;
}
/*
* IF there is only one candidate then return it,
* else return the zero argument one.
*/
if (cnddt_func_list->next) {
while (cnddt_func_list) {
if (0 == cnddt_func_list->nargs)
break;
cnddt_func_list = cnddt_func_list->next;
}
if (cnddt_func_list == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s asks parameters", NameListToString(funcname))));
}
if (OidIsValid(cnddt_func_list->oid)) {
return cnddt_func_list->oid;
}
/* In concurrence, can appear invalid Oid.*/
else {
if (!noError)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist", NameListToString(funcname))));
return InvalidOid;
}
}
/*
* 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 = NULL;
Oid oid;
HeapTuple ftup;
Form_pg_proc pform;
argcount = list_length(argtypes);
if (argcount > FUNC_MAX_ARGS)
ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg_plural("functions cannot have more than %d argument",
"functions cannot have more than %d arguments",
FUNC_MAX_ARGS,
FUNC_MAX_ARGS)));
i = 0;
foreach (lc, argtypes) {
TypeName* t = (TypeName*)lfirst(lc);
argoids[i] = LookupTypeNameOid(t);
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))));
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("aggregate %s does not exist", func_signature_string(aggname, argcount, NIL, argoids))));
}
/* Make sure it's an aggregate */
ftup = SearchSysCache1(PROCOID, ObjectIdGetDatum(oid));
if (!HeapTupleIsValid(ftup)) /* should not happen */
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", oid)));
pform = (Form_pg_proc)GETSTRUCT(ftup);
if (!pform->proisagg) {
ReleaseSysCache(ftup);
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, NIL, argoids))));
}
ReleaseSysCache(ftup);
return oid;
}
// fetch default args if caller wants 'em
static List* GetDefaultVale(Oid funcoid, const int* argnumbers, int ndargs)
{
HeapTuple tuple;
Form_pg_proc formproc;
int pronallargs = 0;
int pronargdefaults = 0;
Oid* argtypes = NULL;
char** argnames = NULL;
char* argmodes = NULL;
List* defaults = NIL;
Datum defargposdatum;
int2vector* defaultargpos = NULL;
Datum proargdefaults;
char* defaultsstr = NULL;
int pronargs = 0;
bool isnull = false;
bool found = true;
int* defaultpos = NULL;
int counter1 = 0;
int counter2 = 0;
int pos = 0;
if (0 == ndargs)
return defaults;
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
if (!HeapTupleIsValid(tuple)) {
ereport(
ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("cache lookup failed for function \"%u\"", funcoid)));
return defaults;
}
pronallargs = get_func_arg_info(tuple, &argtypes, &argnames, &argmodes);
formproc = (Form_pg_proc)GETSTRUCT(tuple);
pronargs = formproc->pronargs;
pronargdefaults = formproc->pronargdefaults;
proargdefaults = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proargdefaults, &isnull);
AssertEreport(!isnull, MOD_OPT, "");
defaultsstr = TextDatumGetCString(proargdefaults);
defaults = (List*)stringToNode(defaultsstr);
AssertEreport(IsA(defaults, List), MOD_OPT, "");
pfree_ext(defaultsstr);
if (pronargs <= FUNC_MAX_ARGS_INROW) {
defargposdatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prodefaultargpos, &isnull);
AssertEreport(!isnull, MOD_OPT, "");
defaultargpos = (int2vector*)DatumGetPointer(defargposdatum);
} else {
defargposdatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prodefaultargposext, &isnull);
AssertEreport(!isnull, MOD_OPT, "");
defaultargpos = (int2vector*)PG_DETOAST_DATUM(defargposdatum);
}
AssertEreport(pronargs >= ndargs, MOD_OPT, "");
AssertEreport(pronargdefaults >= ndargs, MOD_OPT, "");
AssertEreport(pronallargs >= pronargs, MOD_OPT, "");
FetchDefaultArgumentPos(&defaultpos, defaultargpos, argmodes, pronallargs);
/*
* This is a bit tricky in named notation, since the supplied
* arguments could replace any subset of the defaults.
*/
if (argnumbers != NULL) {
bool* deleteflag = NULL;
int rc = 0;
deleteflag = (bool*)palloc(pronargdefaults * sizeof(bool));
rc = memset_s(deleteflag, pronargdefaults * sizeof(bool), 0, pronargdefaults * sizeof(bool));
securec_check_c(rc, "\0", "\0");
for (counter1 = pronargdefaults; counter1 > 0; counter1--) {
found = false;
pos = defaultpos[counter1 - 1];
for (counter2 = ndargs; counter2 > 0; counter2--) {
if (argnumbers[pronargs - counter2] == pos) {
found = true;
break;
}
}
if (!found)
defaults = RemoveListCell(defaults, pos);
}
}
/*
* Defaults for positional notation are lots easier; just
* remove any unwanted ones from the front.
*/
else {
int ndelete = 0;
ndelete = pronargdefaults - ndargs;
while (ndelete-- > 0) {
defaults = list_delete_first(defaults);
}
}
if (argtypes != NULL)
pfree_ext(argtypes);
if (argmodes != NULL)
pfree_ext(argmodes);
if (argnames != NULL) {
for (counter1 = 0; counter1 < pronallargs; counter1++) {
if (argnames[counter1])
pfree_ext(argnames[counter1]);
}
pfree_ext(argnames);
}
pfree_ext(defaultpos);
ReleaseSysCache(tuple);
return defaults;
}
/*
* Replace column managed type with original type
* to identify overloaded functions
*/
static Oid cl_get_input_param_original_type(Oid func_id, int argno)
{
HeapTuple gs_oldtup = SearchSysCache1(GSCLPROCID, ObjectIdGetDatum(func_id));
Oid ret = InvalidOid;
if (HeapTupleIsValid(gs_oldtup)) {
bool isnull = false;
oidvector* proargcachedcol = (oidvector*)DatumGetPointer(
SysCacheGetAttr(GSCLPROCID, gs_oldtup, Anum_gs_encrypted_proc_proargcachedcol, &isnull));
if (!isnull && proargcachedcol->dim1 > argno) {
Oid cachedColId = proargcachedcol->values[argno];
HeapTuple tup = SearchSysCache1(CEOID, ObjectIdGetDatum(cachedColId));
if (HeapTupleIsValid(tup)) {
Form_gs_encrypted_columns ec_form = (Form_gs_encrypted_columns)GETSTRUCT(tup);
ret = ec_form->data_type_original_oid;
ReleaseSysCache(tup);
}
}
ReleaseSysCache(gs_oldtup);
}
return ret;
}