Commit 75b39e79 authored by Tom Lane's avatar Tom Lane

Add infrastructure for storing a VARIADIC ANY function's VARIADIC flag.

Originally we didn't bother to mark FuncExprs with any indication whether
VARIADIC had been given in the source text, because there didn't seem to be
any need for it at runtime.  However, because we cannot fold a VARIADIC ANY
function's arguments into an array (since they're not necessarily all the
same type), we do actually need that information at runtime if VARIADIC ANY
functions are to respond unsurprisingly to use of the VARIADIC keyword.
Add the missing field, and also fix ruleutils.c so that VARIADIC ANY
function calls are dumped properly.

Extracted from a larger patch that also fixes concat() and format() (the
only two extant VARIADIC ANY functions) to behave properly when VARIADIC is
specified.  This portion seems appropriate to review and commit separately.

Pavel Stehule
parent 841a5150
......@@ -1194,6 +1194,7 @@ _copyFuncExpr(const FuncExpr *from)
COPY_SCALAR_FIELD(funcid);
COPY_SCALAR_FIELD(funcresulttype);
COPY_SCALAR_FIELD(funcretset);
COPY_SCALAR_FIELD(funcvariadic);
COPY_SCALAR_FIELD(funcformat);
COPY_SCALAR_FIELD(funccollid);
COPY_SCALAR_FIELD(inputcollid);
......
......@@ -239,6 +239,7 @@ _equalFuncExpr(const FuncExpr *a, const FuncExpr *b)
COMPARE_SCALAR_FIELD(funcid);
COMPARE_SCALAR_FIELD(funcresulttype);
COMPARE_SCALAR_FIELD(funcretset);
COMPARE_SCALAR_FIELD(funcvariadic);
COMPARE_COERCIONFORM_FIELD(funcformat);
COMPARE_SCALAR_FIELD(funccollid);
COMPARE_SCALAR_FIELD(inputcollid);
......
......@@ -461,6 +461,7 @@ makeFuncExpr(Oid funcid, Oid rettype, List *args,
funcexpr->funcid = funcid;
funcexpr->funcresulttype = rettype;
funcexpr->funcretset = false; /* only allowed case here */
funcexpr->funcvariadic = false; /* only allowed case here */
funcexpr->funcformat = fformat;
funcexpr->funccollid = funccollid;
funcexpr->inputcollid = inputcollid;
......
......@@ -1000,6 +1000,7 @@ _outFuncExpr(StringInfo str, const FuncExpr *node)
WRITE_OID_FIELD(funcid);
WRITE_OID_FIELD(funcresulttype);
WRITE_BOOL_FIELD(funcretset);
WRITE_BOOL_FIELD(funcvariadic);
WRITE_ENUM_FIELD(funcformat, CoercionForm);
WRITE_OID_FIELD(funccollid);
WRITE_OID_FIELD(inputcollid);
......
......@@ -537,6 +537,7 @@ _readFuncExpr(void)
READ_OID_FIELD(funcid);
READ_OID_FIELD(funcresulttype);
READ_BOOL_FIELD(funcretset);
READ_BOOL_FIELD(funcvariadic);
READ_ENUM_FIELD(funcformat, CoercionForm);
READ_OID_FIELD(funccollid);
READ_OID_FIELD(inputcollid);
......
......@@ -110,7 +110,7 @@ static Node *simplify_boolean_equality(Oid opno, List *args);
static Expr *simplify_function(Oid funcid,
Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List **args_p,
bool process_args, bool allow_non_const,
bool funcvariadic, bool process_args, bool allow_non_const,
eval_const_expressions_context *context);
static List *expand_function_arguments(List *args, Oid result_type,
HeapTuple func_tuple);
......@@ -121,10 +121,12 @@ static void recheck_cast_function_args(List *args, Oid result_type,
HeapTuple func_tuple);
static Expr *evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List *args,
bool funcvariadic,
HeapTuple func_tuple,
eval_const_expressions_context *context);
static Expr *inline_function(Oid funcid, Oid result_type, Oid result_collid,
Oid input_collid, List *args,
bool funcvariadic,
HeapTuple func_tuple,
eval_const_expressions_context *context);
static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
......@@ -2314,6 +2316,7 @@ eval_const_expressions_mutator(Node *node,
expr->funccollid,
expr->inputcollid,
&args,
expr->funcvariadic,
true,
true,
context);
......@@ -2330,6 +2333,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->funcid = expr->funcid;
newexpr->funcresulttype = expr->funcresulttype;
newexpr->funcretset = expr->funcretset;
newexpr->funcvariadic = expr->funcvariadic;
newexpr->funcformat = expr->funcformat;
newexpr->funccollid = expr->funccollid;
newexpr->inputcollid = expr->inputcollid;
......@@ -2359,6 +2363,7 @@ eval_const_expressions_mutator(Node *node,
expr->opcollid,
expr->inputcollid,
&args,
false,
true,
true,
context);
......@@ -2464,6 +2469,7 @@ eval_const_expressions_mutator(Node *node,
&args,
false,
false,
false,
context);
if (simple) /* successfully simplified it */
{
......@@ -2665,6 +2671,7 @@ eval_const_expressions_mutator(Node *node,
InvalidOid,
InvalidOid,
&args,
false,
true,
true,
context);
......@@ -2697,6 +2704,7 @@ eval_const_expressions_mutator(Node *node,
InvalidOid,
&args,
false,
false,
true,
context);
if (simple) /* successfully simplified input fn */
......@@ -3565,7 +3573,7 @@ simplify_boolean_equality(Oid opno, List *args)
static Expr *
simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List **args_p,
bool process_args, bool allow_non_const,
bool funcvariadic, bool process_args, bool allow_non_const,
eval_const_expressions_context *context)
{
List *args = *args_p;
......@@ -3609,7 +3617,8 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
/* Now attempt simplification of the function call proper. */
newexpr = evaluate_function(funcid, result_type, result_typmod,
result_collid, input_collid, args,
result_collid, input_collid,
args, funcvariadic,
func_tuple, context);
if (!newexpr && allow_non_const && OidIsValid(func_form->protransform))
......@@ -3625,6 +3634,7 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
fexpr.funcid = funcid;
fexpr.funcresulttype = result_type;
fexpr.funcretset = func_form->proretset;
fexpr.funcvariadic = funcvariadic;
fexpr.funcformat = COERCE_EXPLICIT_CALL;
fexpr.funccollid = result_collid;
fexpr.inputcollid = input_collid;
......@@ -3638,7 +3648,7 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
if (!newexpr && allow_non_const)
newexpr = inline_function(funcid, result_type, result_collid,
input_collid, args,
input_collid, args, funcvariadic,
func_tuple, context);
ReleaseSysCache(func_tuple);
......@@ -3878,6 +3888,7 @@ recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
static Expr *
evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List *args,
bool funcvariadic,
HeapTuple func_tuple,
eval_const_expressions_context *context)
{
......@@ -3959,6 +3970,7 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
newexpr->funcid = funcid;
newexpr->funcresulttype = result_type;
newexpr->funcretset = false;
newexpr->funcvariadic = funcvariadic;
newexpr->funcformat = COERCE_EXPLICIT_CALL; /* doesn't matter */
newexpr->funccollid = result_collid; /* doesn't matter */
newexpr->inputcollid = input_collid;
......@@ -4001,6 +4013,7 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
static Expr *
inline_function(Oid funcid, Oid result_type, Oid result_collid,
Oid input_collid, List *args,
bool funcvariadic,
HeapTuple func_tuple,
eval_const_expressions_context *context)
{
......@@ -4089,6 +4102,7 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
fexpr->funcid = funcid;
fexpr->funcresulttype = result_type;
fexpr->funcretset = false;
fexpr->funcvariadic = funcvariadic;
fexpr->funcformat = COERCE_EXPLICIT_CALL; /* doesn't matter */
fexpr->funccollid = result_collid; /* doesn't matter */
fexpr->inputcollid = input_collid;
......
......@@ -384,6 +384,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
funcexpr->funcid = funcid;
funcexpr->funcresulttype = rettype;
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;
......
......@@ -396,8 +396,9 @@ static Node *processIndirection(Node *node, deparse_context *context,
static void printSubscripts(ArrayRef *aref, deparse_context *context);
static char *get_relation_name(Oid relid);
static char *generate_relation_name(Oid relid, List *namespaces);
static char *generate_function_name(Oid funcid, int nargs, List *argnames,
Oid *argtypes, bool *is_variadic);
static char *generate_function_name(Oid funcid, int nargs,
List *argnames, Oid *argtypes,
bool was_variadic, bool *use_variadic_p);
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
static text *string_to_text(char *str);
static char *flatten_reloptions(Oid relid);
......@@ -858,7 +859,8 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
generate_function_name(trigrec->tgfoid, 0,
NIL, NULL, NULL));
NIL, NULL,
false, NULL));
if (trigrec->tgnargs > 0)
{
......@@ -7269,7 +7271,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
Oid argtypes[FUNC_MAX_ARGS];
int nargs;
List *argnames;
bool is_variadic;
bool use_variadic;
ListCell *l;
/*
......@@ -7327,13 +7329,14 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
appendStringInfo(buf, "%s(",
generate_function_name(funcoid, nargs,
argnames, argtypes,
&is_variadic));
expr->funcvariadic,
&use_variadic));
nargs = 0;
foreach(l, expr->args)
{
if (nargs++ > 0)
appendStringInfoString(buf, ", ");
if (is_variadic && lnext(l) == NULL)
if (use_variadic && lnext(l) == NULL)
appendStringInfoString(buf, "VARIADIC ");
get_rule_expr((Node *) lfirst(l), context, true);
}
......@@ -7374,7 +7377,8 @@ get_agg_expr(Aggref *aggref, deparse_context *context)
appendStringInfo(buf, "%s(%s",
generate_function_name(aggref->aggfnoid, nargs,
NIL, argtypes, NULL),
NIL, argtypes,
false, NULL),
(aggref->aggdistinct != NIL) ? "DISTINCT " : "");
/* aggstar can be set only in zero-argument aggregates */
if (aggref->aggstar)
......@@ -7416,7 +7420,8 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
appendStringInfo(buf, "%s(",
generate_function_name(wfunc->winfnoid, nargs,
NIL, argtypes, NULL));
NIL, argtypes,
false, NULL));
/* winstar can be set only in zero-argument aggregates */
if (wfunc->winstar)
appendStringInfoChar(buf, '*');
......@@ -8507,18 +8512,25 @@ generate_relation_name(Oid relid, List *namespaces)
* given that it is being called with the specified actual arg names and
* types. (Those matter because of ambiguous-function resolution rules.)
*
* The result includes all necessary quoting and schema-prefixing. We can
* also pass back an indication of whether the function is variadic.
* If we're dealing with a potentially variadic function (in practice, this
* means a FuncExpr and not some other way of calling the function), then
* was_variadic must specify whether VARIADIC appeared in the original call,
* and *use_variadic_p will be set to indicate whether to print VARIADIC in
* the output. For non-FuncExpr cases, was_variadic should be FALSE and
* use_variadic_p can be NULL.
*
* The result includes all necessary quoting and schema-prefixing.
*/
static char *
generate_function_name(Oid funcid, int nargs, List *argnames,
Oid *argtypes, bool *is_variadic)
generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
bool was_variadic, bool *use_variadic_p)
{
char *result;
HeapTuple proctup;
Form_pg_proc procform;
char *proname;
bool use_variadic;
char *nspname;
char *result;
FuncDetailCode p_result;
Oid p_funcid;
Oid p_rettype;
......@@ -8532,15 +8544,47 @@ generate_function_name(Oid funcid, int nargs, List *argnames,
procform = (Form_pg_proc) GETSTRUCT(proctup);
proname = NameStr(procform->proname);
/*
* Determine whether VARIADIC should be printed. We must do this first
* since it affects the lookup rules in func_get_detail().
*
* Currently, we always print VARIADIC if the function is variadic and
* takes a variadic type other than ANY. (In principle, if VARIADIC
* wasn't originally specified and the array actual argument is
* deconstructable, we could print the array elements separately and not
* print VARIADIC, thus more nearly reproducing the original input. For
* the moment that seems like too much complication for the benefit.)
* However, if the function takes VARIADIC ANY, then the parser didn't
* fold the arguments together into an array, so we must print VARIADIC if
* and only if it was used originally.
*/
if (use_variadic_p)
{
if (OidIsValid(procform->provariadic))
{
if (procform->provariadic != ANYOID)
use_variadic = true;
else
use_variadic = was_variadic;
}
else
use_variadic = false;
*use_variadic_p = use_variadic;
}
else
{
Assert(!was_variadic);
use_variadic = false;
}
/*
* The idea here is to schema-qualify only if the parser would fail to
* resolve the correct function given the unqualified func name with the
* specified argtypes. If the function is variadic, we should presume
* that VARIADIC will be included in the call.
* specified argtypes and VARIADIC flag.
*/
p_result = func_get_detail(list_make1(makeString(proname)),
NIL, argnames, nargs, argtypes,
!OidIsValid(procform->provariadic), true,
!use_variadic, true,
&p_funcid, &p_rettype,
&p_retset, &p_nvargs, &p_true_typeids, NULL);
if ((p_result == FUNCDETAIL_NORMAL ||
......@@ -8553,17 +8597,6 @@ generate_function_name(Oid funcid, int nargs, List *argnames,
result = quote_qualified_identifier(nspname, proname);
/* Check variadic-ness if caller cares */
if (is_variadic)
{
/* "any" variadics are not treated as variadics for listing */
if (OidIsValid(procform->provariadic) &&
procform->provariadic != ANYOID)
*is_variadic = true;
else
*is_variadic = false;
}
ReleaseSysCache(proctup);
return result;
......
......@@ -2281,6 +2281,7 @@ pg_detoast_datum_packed(struct varlena * datum)
* These are needed by polymorphic functions, which accept multiple possible
* input types and need help from the parser to know what they've got.
* Also, some functions might be interested in whether a parameter is constant.
* Functions taking VARIADIC ANY also need to know about the VARIADIC keyword.
*-------------------------------------------------------------------------
*/
......@@ -2445,3 +2446,28 @@ get_call_expr_arg_stable(Node *expr, int argnum)
return false;
}
/*
* Get the VARIADIC flag from the function invocation
*
* Returns false (the default assumption) if information is not available
*/
bool
get_fn_expr_variadic(FmgrInfo *flinfo)
{
Node *expr;
/*
* can't return anything useful if we have no FmgrInfo or if its fn_expr
* node has not been initialized
*/
if (!flinfo || !flinfo->fn_expr)
return false;
expr = flinfo->fn_expr;
if (IsA(expr, FuncExpr))
return ((FuncExpr *) expr)->funcvariadic;
else
return false;
}
......@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201301171
#define CATALOG_VERSION_NO 201301211
#endif
......@@ -624,6 +624,7 @@ extern Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum);
extern Oid get_call_expr_argtype(fmNodePtr expr, int argnum);
extern bool get_fn_expr_arg_stable(FmgrInfo *flinfo, int argnum);
extern bool get_call_expr_arg_stable(fmNodePtr expr, int argnum);
extern bool get_fn_expr_variadic(FmgrInfo *flinfo);
/*
* Routines in dfmgr.c
......
......@@ -340,6 +340,7 @@ typedef struct FuncExpr
Oid funcid; /* PG_PROC OID of the function */
Oid funcresulttype; /* PG_TYPE OID of result value */
bool funcretset; /* true if function returns set */
bool funcvariadic; /* true if VARIADIC was used in call */
CoercionForm funcformat; /* how to display this function call */
Oid funccollid; /* OID of collation of result */
Oid inputcollid; /* OID of collation that function should use */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment