From 75b39e79099fb1da43542e12698df717d1bd120f Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 21 Jan 2013 20:25:26 -0500 Subject: [PATCH] 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 --- src/backend/nodes/copyfuncs.c | 1 + src/backend/nodes/equalfuncs.c | 1 + src/backend/nodes/makefuncs.c | 1 + src/backend/nodes/outfuncs.c | 1 + src/backend/nodes/readfuncs.c | 1 + src/backend/optimizer/util/clauses.c | 22 +++++-- src/backend/parser/parse_func.c | 1 + src/backend/utils/adt/ruleutils.c | 87 +++++++++++++++++++--------- src/backend/utils/fmgr/fmgr.c | 26 +++++++++ src/include/catalog/catversion.h | 2 +- src/include/fmgr.h | 1 + src/include/nodes/primnodes.h | 1 + 12 files changed, 113 insertions(+), 32 deletions(-) diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 51fdb63cbd..9a01ec6d59 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -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); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 4b219b35ba..034159da31 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -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); diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 7b8ac57cd6..c487db96d8 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -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; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 3cce5f517e..484e426489 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -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); diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index e7c6ad67e9..ed2354144c 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -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); diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 55c4a13644..657a18b1be 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -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; diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index c7a2a22465..ae7d195a3e 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -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; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 266cec5ffa..af10471581 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -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; diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index e21bed83e3..42de04c60a 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -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; +} diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index cd562ef40c..a676793566 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201301171 +#define CATALOG_VERSION_NO 201301211 #endif diff --git a/src/include/fmgr.h b/src/include/fmgr.h index fe4a41ba09..1f72e1bd48 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -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 diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index ac53e463fc..1d657669e1 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -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 */ -- 2.40.0