1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-26 01:22:12 +03:00

Code review for function default parameters patch. Fix numerous problems as

per recent discussions.  In passing this also fixes a couple of bugs in
the previous variadic-parameters patch.
This commit is contained in:
Tom Lane
2008-12-18 18:20:35 +00:00
parent cee63eab8d
commit 517ae4039e
27 changed files with 803 additions and 445 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.246 2008/10/22 20:17:51 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.247 2008/12/18 18:20:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -513,27 +513,18 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
/*
* Simplify constant expressions.
*
* Note: one essential effect here is to insert the current actual values
* of any default arguments for functions. To ensure that happens, we
* *must* process all expressions here. Previous PG versions sometimes
* skipped const-simplification if it didn't seem worth the trouble, but
* we can't do that anymore.
*
* Note: this also flattens nested AND and OR expressions into N-argument
* form. All processing of a qual expression after this point must be
* careful to maintain AND/OR flatness --- that is, do not generate a tree
* with AND directly under AND, nor OR directly under OR.
*
* Because this is a relatively expensive process, we skip it when the
* query is trivial, such as "SELECT 2+2;" or "INSERT ... VALUES()". The
* expression will only be evaluated once anyway, so no point in
* pre-simplifying; we can't execute it any faster than the executor can,
* and we will waste cycles copying the tree. Notice however that we
* still must do it for quals (to get AND/OR flatness); and if we are in a
* subquery we should not assume it will be done only once.
*
* For VALUES lists we never do this at all, again on the grounds that we
* should optimize for one-time evaluation.
*/
if (kind != EXPRKIND_VALUES &&
(root->parse->jointree->fromlist != NIL ||
kind == EXPRKIND_QUAL ||
root->query_level > 1))
expr = eval_const_expressions(root, expr);
expr = eval_const_expressions(root, expr);
/*
* If it's a qual or havingQual, canonicalize it.

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.270 2008/10/21 20:42:53 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.271 2008/12/18 18:20:34 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -36,6 +36,7 @@
#include "optimizer/var.h"
#include "parser/analyze.h"
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
#include "rewrite/rewriteManip.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
@ -91,9 +92,11 @@ static List *simplify_and_arguments(List *args,
bool *haveNull, bool *forceFalse);
static Expr *simplify_boolean_equality(List *args);
static Expr *simplify_function(Oid funcid,
Oid result_type, int32 result_typmod, List *args,
Oid result_type, int32 result_typmod, List **args,
bool allow_inline,
eval_const_expressions_context *context);
static List *add_function_defaults(List *args, Oid result_type,
HeapTuple func_tuple);
static Expr *evaluate_function(Oid funcid,
Oid result_type, int32 result_typmod, List *args,
HeapTuple func_tuple,
@ -2025,7 +2028,7 @@ eval_const_expressions_mutator(Node *node,
*/
simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node),
args,
&args,
true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
@ -2072,7 +2075,7 @@ eval_const_expressions_mutator(Node *node,
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
args,
&args,
true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
@ -2163,7 +2166,7 @@ eval_const_expressions_mutator(Node *node,
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
args,
&args,
false, context);
if (simple) /* successfully simplified it */
{
@ -2329,6 +2332,7 @@ eval_const_expressions_mutator(Node *node,
{
CoerceViaIO *expr = (CoerceViaIO *) node;
Expr *arg;
List *args;
Oid outfunc;
bool outtypisvarlena;
Oid infunc;
@ -2341,6 +2345,7 @@ eval_const_expressions_mutator(Node *node,
*/
arg = (Expr *) eval_const_expressions_mutator((Node *) expr->arg,
context);
args = list_make1(arg);
/*
* CoerceViaIO represents calling the source type's output function
@ -2353,7 +2358,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(outfunc,
CSTRINGOID, -1,
list_make1(arg),
&args,
true, context);
if (simple) /* successfully simplified output fn */
{
@ -2361,8 +2366,6 @@ eval_const_expressions_mutator(Node *node,
* Input functions may want 1 to 3 arguments. We always supply
* all three, trusting that nothing downstream will complain.
*/
List *args;
args = list_make3(simple,
makeConst(OIDOID, -1, sizeof(Oid),
ObjectIdGetDatum(intypioparam),
@ -2373,7 +2376,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function(infunc,
expr->resulttype, -1,
args,
&args,
true, context);
if (simple) /* successfully simplified input fn */
return (Node *) simple;
@ -3126,10 +3129,16 @@ simplify_boolean_equality(List *args)
*
* Returns a simplified expression if successful, or NULL if cannot
* simplify the function call.
*
* This function is also responsible for adding any default argument
* expressions onto the function argument list; which is a bit grotty,
* but it avoids an extra fetch of the function's pg_proc tuple. For this
* reason, the args list is pass-by-reference, and it may get modified
* even if simplification fails.
*/
static Expr *
simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
List *args,
List **args,
bool allow_inline,
eval_const_expressions_context *context)
{
@ -3150,11 +3159,15 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
if (!HeapTupleIsValid(func_tuple))
elog(ERROR, "cache lookup failed for function %u", funcid);
newexpr = evaluate_function(funcid, result_type, result_typmod, args,
/* While we have the tuple, check if we need to add defaults */
if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
*args = add_function_defaults(*args, result_type, func_tuple);
newexpr = evaluate_function(funcid, result_type, result_typmod, *args,
func_tuple, context);
if (!newexpr && allow_inline)
newexpr = inline_function(funcid, result_type, args,
newexpr = inline_function(funcid, result_type, *args,
func_tuple, context);
ReleaseSysCache(func_tuple);
@ -3162,6 +3175,77 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
return newexpr;
}
/*
* add_function_defaults: add missing function arguments from its defaults
*
* It is possible for some of the defaulted arguments to be polymorphic;
* therefore we can't assume that the default expressions have the correct
* data types already. We have to re-resolve polymorphics and do coercion
* just like the parser did.
*/
static List *
add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
Datum proargdefaults;
bool isnull;
char *str;
List *defaults;
int ndelete;
int nargs;
Oid actual_arg_types[FUNC_MAX_ARGS];
Oid declared_arg_types[FUNC_MAX_ARGS];
Oid rettype;
ListCell *lc;
/* The error cases here shouldn't happen, but check anyway */
proargdefaults = SysCacheGetAttr(PROCOID, func_tuple,
Anum_pg_proc_proargdefaults,
&isnull);
if (isnull)
elog(ERROR, "not enough default arguments");
str = TextDatumGetCString(proargdefaults);
defaults = (List *) stringToNode(str);
Assert(IsA(defaults, List));
pfree(str);
/* Delete any unused defaults from the list */
ndelete = list_length(args) + list_length(defaults) - funcform->pronargs;
if (ndelete < 0)
elog(ERROR, "not enough default arguments");
while (ndelete-- > 0)
defaults = list_delete_first(defaults);
/* And form the combined argument list */
args = list_concat(args, defaults);
Assert(list_length(args) == funcform->pronargs);
/*
* The rest of this should be a no-op if there are no polymorphic
* arguments, but we do it anyway to be sure.
*/
if (list_length(args) > FUNC_MAX_ARGS)
elog(ERROR, "too many function arguments");
nargs = 0;
foreach(lc, args)
{
actual_arg_types[nargs++] = exprType((Node *) lfirst(lc));
}
memcpy(declared_arg_types, funcform->proargtypes.values,
funcform->pronargs * sizeof(Oid));
rettype = enforce_generic_type_consistency(actual_arg_types,
declared_arg_types,
nargs,
funcform->prorettype,
false);
/* let's just check we got the same answer as the parser did ... */
if (rettype != result_type)
elog(ERROR, "function's resolved result type changed during planning");
/* perform any necessary typecasting of arguments */
make_fn_arguments(NULL, args, actual_arg_types, declared_arg_types);
return args;
}
/*
* evaluate_function: try to pre-evaluate a function call
*