Hi list, Per Tom's request, I split out this refactoring from my CacheExpr patch.
Basically I'm just centralizing the eval_const_expressions_mutator() call on function arguments, from multiple different places to a single location. Without this, it would be a lot harder to implement argument caching logic in the CacheExpr patch. The old call tree goes like: case T_FuncExpr: -> eval_const_expressions_mutator(args) -> simplify_function -> reorder_function_arguments -> eval_const_expressions_mutator(args) -> add_function_defaults -> eval_const_expressions_mutator(args) New call tree: case T_FuncExpr: -> simplify_function -> simplify_copy_function_arguments -> reorder_function_arguments -> add_function_defaults -> eval_const_expressions_mutator(args) Assumptions being made: * The code didn't depend on expanding existing arguments before adding defaults * operator calls don't need to expand default arguments -- it's currently impossible to CREATE OPERATOR with a function that has unspecified arguments Other changes: * simplify_function no longer needs a 'bool has_named_args' argument (it finds out itself). * I added 'bool mutate_args' in its place, since some callers need to mutate args beforehand. * reorder_function_arguments no longer needs to keep track of which default args were added. Regards, Marti
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c new file mode 100644 index cd3da46..9485e95 *** a/src/backend/optimizer/util/clauses.c --- b/src/backend/optimizer/util/clauses.c *************** static List *simplify_and_arguments(List *** 109,124 **** static Node *simplify_boolean_equality(Oid opno, List *args); static Expr *simplify_function(Expr *oldexpr, Oid funcid, Oid result_type, int32 result_typmod, Oid result_collid, ! Oid input_collid, List **args, ! bool has_named_args, ! bool allow_inline, eval_const_expressions_context *context); static List *reorder_function_arguments(List *args, Oid result_type, ! HeapTuple func_tuple, ! eval_const_expressions_context *context); static List *add_function_defaults(List *args, Oid result_type, ! HeapTuple func_tuple, ! eval_const_expressions_context *context); static List *fetch_function_defaults(HeapTuple func_tuple); static void recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple); --- 109,123 ---- static Node *simplify_boolean_equality(Oid opno, List *args); static Expr *simplify_function(Expr *oldexpr, Oid funcid, Oid result_type, int32 result_typmod, Oid result_collid, ! Oid input_collid, List **args_p, ! bool mutate_args, bool allow_inline, eval_const_expressions_context *context); + static List *simplify_copy_function_arguments(List *old_args, Oid result_type, + HeapTuple func_tuple); static List *reorder_function_arguments(List *args, Oid result_type, ! HeapTuple func_tuple); static List *add_function_defaults(List *args, Oid result_type, ! HeapTuple func_tuple); static List *fetch_function_defaults(HeapTuple func_tuple); static void recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple); *************** eval_const_expressions_mutator(Node *nod *** 2303,2336 **** case T_FuncExpr: { FuncExpr *expr = (FuncExpr *) node; ! List *args; ! bool has_named_args; Expr *simple; FuncExpr *newexpr; - ListCell *lc; - - /* - * Reduce constants in the FuncExpr's arguments, and check to - * see if there are any named args. - */ - args = NIL; - has_named_args = false; - foreach(lc, expr->args) - { - Node *arg = (Node *) lfirst(lc); - - arg = eval_const_expressions_mutator(arg, context); - if (IsA(arg, NamedArgExpr)) - has_named_args = true; - args = lappend(args, arg); - } /* * Code for op/func reduction is pretty bulky, so split it out * as a separate function. Note: exprTypmod normally returns * -1 for a FuncExpr, but not when the node is recognizably a * length coercion; we want to preserve the typmod in the ! * eventual Const if so. */ simple = simplify_function((Expr *) expr, expr->funcid, --- 2302,2318 ---- case T_FuncExpr: { FuncExpr *expr = (FuncExpr *) node; ! List *args = expr->args; Expr *simple; FuncExpr *newexpr; /* * Code for op/func reduction is pretty bulky, so split it out * as a separate function. Note: exprTypmod normally returns * -1 for a FuncExpr, but not when the node is recognizably a * length coercion; we want to preserve the typmod in the ! * eventual Const if so. This function also mutates the ! * argument list. */ simple = simplify_function((Expr *) expr, expr->funcid, *************** eval_const_expressions_mutator(Node *nod *** 2339,2345 **** expr->funccollid, expr->inputcollid, &args, ! has_named_args, true, context); if (simple) /* successfully simplified it */ --- 2321,2327 ---- expr->funccollid, expr->inputcollid, &args, ! true, true, context); if (simple) /* successfully simplified it */ *************** eval_const_expressions_mutator(Node *nod *** 2365,2385 **** case T_OpExpr: { OpExpr *expr = (OpExpr *) node; ! List *args; Expr *simple; OpExpr *newexpr; /* - * Reduce constants in the OpExpr's arguments. We know args - * is either NIL or a List node, so we can call - * expression_tree_mutator directly rather than recursing to - * self. - */ - args = (List *) expression_tree_mutator((Node *) expr->args, - eval_const_expressions_mutator, - (void *) context); - - /* * Need to get OID of underlying function. Okay to scribble * on input to this extent. */ --- 2347,2357 ---- case T_OpExpr: { OpExpr *expr = (OpExpr *) node; ! List *args = expr->args; Expr *simple; OpExpr *newexpr; /* * Need to get OID of underlying function. Okay to scribble * on input to this extent. */ *************** eval_const_expressions_mutator(Node *nod *** 2387,2393 **** /* * Code for op/func reduction is pretty bulky, so split it out ! * as a separate function. */ simple = simplify_function((Expr *) expr, expr->opfuncid, --- 2359,2366 ---- /* * Code for op/func reduction is pretty bulky, so split it out ! * as a separate function. This function also mutates the ! * argument list. */ simple = simplify_function((Expr *) expr, expr->opfuncid, *************** eval_const_expressions_mutator(Node *nod *** 2395,2401 **** expr->opcollid, expr->inputcollid, &args, ! false, true, context); if (simple) /* successfully simplified it */ return (Node *) simple; --- 2368,2374 ---- expr->opcollid, expr->inputcollid, &args, ! true, true, context); if (simple) /* successfully simplified it */ return (Node *) simple; *************** eval_const_expressions_mutator(Node *nod *** 2668,2674 **** case T_CoerceViaIO: { CoerceViaIO *expr = (CoerceViaIO *) node; - Expr *arg; List *args; Oid outfunc; bool outtypisvarlena; --- 2641,2646 ---- *************** eval_const_expressions_mutator(Node *nod *** 2677,2688 **** Expr *simple; CoerceViaIO *newexpr; ! /* ! * Reduce constants in the CoerceViaIO's argument. ! */ ! arg = (Expr *) eval_const_expressions_mutator((Node *) expr->arg, ! context); ! args = list_make1(arg); /* * CoerceViaIO represents calling the source type's output --- 2649,2655 ---- Expr *simple; CoerceViaIO *newexpr; ! args = list_make1(expr->arg); /* * CoerceViaIO represents calling the source type's output *************** eval_const_expressions_mutator(Node *nod *** 2693,2710 **** * Note that the coercion functions are assumed not to care * about input collation, so we just pass InvalidOid for that. */ ! getTypeOutputInfo(exprType((Node *) arg), &outfunc, &outtypisvarlena); getTypeInputInfo(expr->resulttype, &infunc, &intypioparam); simple = simplify_function(NULL, outfunc, CSTRINGOID, -1, InvalidOid, InvalidOid, &args, ! false, true, context); if (simple) /* successfully simplified output fn */ { /* --- 2660,2678 ---- * Note that the coercion functions are assumed not to care * about input collation, so we just pass InvalidOid for that. */ ! getTypeOutputInfo(exprType((Node *) expr->arg), &outfunc, &outtypisvarlena); getTypeInputInfo(expr->resulttype, &infunc, &intypioparam); + /* Mutate the argument and try to simplify */ simple = simplify_function(NULL, outfunc, CSTRINGOID, -1, InvalidOid, InvalidOid, &args, ! true, true, context); if (simple) /* successfully simplified output fn */ { /* *************** eval_const_expressions_mutator(Node *nod *** 2745,2751 **** * possibly-simplified argument. */ newexpr = makeNode(CoerceViaIO); ! newexpr->arg = arg; newexpr->resulttype = expr->resulttype; newexpr->resultcollid = expr->resultcollid; newexpr->coerceformat = expr->coerceformat; --- 2713,2719 ---- * possibly-simplified argument. */ newexpr = makeNode(CoerceViaIO); ! newexpr->arg = linitial(args); newexpr->resulttype = expr->resulttype; newexpr->resultcollid = expr->resultcollid; newexpr->coerceformat = expr->coerceformat; *************** simplify_boolean_equality(Oid opno, List *** 3584,3590 **** * Inputs are the original expression (can be NULL), function OID, actual * result type OID (which is needed for polymorphic functions), result typmod, * result collation, the input collation to use for the function, the ! * pre-simplified argument list, and some flags; also the context data for * eval_const_expressions. In common cases, several of the arguments could be * derived from the original expression. Sending them separately avoids * duplicating NodeTag-specific knowledge, and it's necessary for CoerceViaIO. --- 3552,3558 ---- * Inputs are the original expression (can be NULL), function OID, actual * result type OID (which is needed for polymorphic functions), result typmod, * result collation, the input collation to use for the function, the ! * un-simplified argument list, and some flags; also the context data for * eval_const_expressions. In common cases, several of the arguments could be * derived from the original expression. Sending them separately avoids * duplicating NodeTag-specific knowledge, and it's necessary for CoerceViaIO. *************** simplify_boolean_equality(Oid opno, List *** 3603,3615 **** static Expr * simplify_function(Expr *oldexpr, Oid funcid, Oid result_type, int32 result_typmod, Oid result_collid, ! Oid input_collid, List **args, ! bool has_named_args, ! bool allow_inline, eval_const_expressions_context *context) { HeapTuple func_tuple; Expr *newexpr; Oid transform; /* --- 3571,3584 ---- static Expr * simplify_function(Expr *oldexpr, Oid funcid, Oid result_type, int32 result_typmod, Oid result_collid, ! Oid input_collid, List **args_p, ! bool mutate_args, bool allow_inline, eval_const_expressions_context *context) { HeapTuple func_tuple; Expr *newexpr; + ListCell *lc; + List *args = *args_p; Oid transform; /* *************** simplify_function(Expr *oldexpr, Oid fun *** 3624,3641 **** if (!HeapTupleIsValid(func_tuple)) elog(ERROR, "cache lookup failed for function %u", funcid); ! /* ! * While we have the tuple, reorder named arguments and add default ! * arguments if needed. ! */ ! if (has_named_args) ! *args = reorder_function_arguments(*args, result_type, func_tuple, ! context); ! else if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args)) ! *args = add_function_defaults(*args, result_type, func_tuple, context); newexpr = evaluate_function(funcid, result_type, result_typmod, ! result_collid, input_collid, *args, func_tuple, context); /* --- 3593,3622 ---- if (!HeapTupleIsValid(func_tuple)) elog(ERROR, "cache lookup failed for function %u", funcid); ! if (mutate_args) ! { ! if (oldexpr && IsA(oldexpr, FuncExpr)) ! /* ! * Reorder named arguments and add defaults if needed. Returns a ! * copied list, so we can mutate it later. ! */ ! args = simplify_copy_function_arguments(args, result_type, func_tuple); ! else ! /* Copy argument list before we start mutating it. */ ! args = list_copy(args); ! ! /* Reduce constants in the expression's arguments */ ! foreach(lc, args) ! { ! Node *arg = (Node *) lfirst(lc); ! ! arg = eval_const_expressions_mutator(arg, context); ! lfirst(lc) = arg; ! } ! } newexpr = evaluate_function(funcid, result_type, result_typmod, ! result_collid, input_collid, args, func_tuple, context); /* *************** simplify_function(Expr *oldexpr, Oid fun *** 3674,3702 **** if (!newexpr && allow_inline) newexpr = inline_function(funcid, result_type, result_collid, ! input_collid, *args, func_tuple, context); ReleaseSysCache(func_tuple); return newexpr; } /* * reorder_function_arguments: convert named-notation args to positional args * * This function also inserts default argument values as needed, since it's * impossible to form a truly valid positional call without that. */ static List * ! reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple, ! eval_const_expressions_context *context) { Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); int pronargs = funcform->pronargs; int nargsprovided = list_length(args); Node *argarray[FUNC_MAX_ARGS]; - Bitmapset *defargnumbers; ListCell *lc; int i; --- 3655,3734 ---- if (!newexpr && allow_inline) newexpr = inline_function(funcid, result_type, result_collid, ! input_collid, args, func_tuple, context); ReleaseSysCache(func_tuple); + /* Argument processing done, give it back to the caller */ + *args_p = args; + return newexpr; } /* + * This function converts a named-notation argument list into positional + * notation while adding any needed default argument expressions + * + * A copy of the argument list is returned, the original is not modified. + */ + static List * + simplify_copy_function_arguments(List *old_args, Oid result_type, + HeapTuple func_tuple) + { + List *args = NIL; + ListCell *lc; + bool has_named_args = false; + int args_before; + + /* Do we need to reorder named arguments? */ + foreach(lc, old_args) + { + Node *arg = (Node *) lfirst(lc); + + if (IsA(arg, NamedArgExpr)) + { + has_named_args = true; + break; + } + } + + args_before = list_length(old_args); + + if (has_named_args) + { + /* Reorder named arguments and add default arguments if needed. */ + args = reorder_function_arguments(old_args, result_type, func_tuple); + } + else + { + args = list_copy(old_args); + + /* Append missing default arguments to the list */ + if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(args)) + args = add_function_defaults(args, result_type, func_tuple); + } + + /* If we added any default args, they may need casts */ + if (list_length(args) != args_before) + recheck_cast_function_args(args, result_type, func_tuple); + + return args; + } + + /* * reorder_function_arguments: convert named-notation args to positional args * * This function also inserts default argument values as needed, since it's * impossible to form a truly valid positional call without that. */ static List * ! reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple) { Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); int pronargs = funcform->pronargs; int nargsprovided = list_length(args); Node *argarray[FUNC_MAX_ARGS]; ListCell *lc; int i; *************** reorder_function_arguments(List *args, O *** 3730,3736 **** * Fetch default expressions, if needed, and insert into array at proper * locations (they aren't necessarily consecutive or all used) */ - defargnumbers = NULL; if (nargsprovided < pronargs) { List *defaults = fetch_function_defaults(func_tuple); --- 3762,3767 ---- *************** reorder_function_arguments(List *args, O *** 3739,3748 **** foreach(lc, defaults) { if (argarray[i] == NULL) - { argarray[i] = (Node *) lfirst(lc); - defargnumbers = bms_add_member(defargnumbers, i); - } i++; } } --- 3770,3776 ---- *************** reorder_function_arguments(List *args, O *** 3755,3786 **** args = lappend(args, argarray[i]); } - /* Recheck argument types and add casts if needed */ - recheck_cast_function_args(args, result_type, func_tuple); - - /* - * Lastly, we have to recursively simplify the defaults we just added (but - * don't recurse on the args passed in, as we already did those). This - * isn't merely an optimization, it's *necessary* since there could be - * functions with named or defaulted arguments down in there. - * - * Note that we do this last in hopes of simplifying any typecasts that - * were added by recheck_cast_function_args --- there shouldn't be any new - * casts added to the explicit arguments, but casts on the defaults are - * possible. - */ - if (defargnumbers != NULL) - { - i = 0; - foreach(lc, args) - { - if (bms_is_member(i, defargnumbers)) - lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc), - context); - i++; - } - } - return args; } --- 3783,3788 ---- *************** reorder_function_arguments(List *args, O *** 3791,3810 **** * and so we know we just need to add defaults at the end. */ static List * ! add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple, ! eval_const_expressions_context *context) { Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); - int nargsprovided = list_length(args); List *defaults; int ndelete; - ListCell *lc; /* Get all the default expressions from the pg_proc tuple */ defaults = fetch_function_defaults(func_tuple); /* Delete any unused defaults from the list */ ! ndelete = nargsprovided + list_length(defaults) - funcform->pronargs; if (ndelete < 0) elog(ERROR, "not enough default arguments"); while (ndelete-- > 0) --- 3793,3809 ---- * and so we know we just need to add defaults at the end. */ static List * ! add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple) { Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); List *defaults; int ndelete; /* Get all the default expressions from the pg_proc tuple */ defaults = fetch_function_defaults(func_tuple); /* 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) *************** add_function_defaults(List *args, Oid re *** 3813,3840 **** /* And form the combined argument list */ args = list_concat(args, defaults); - /* Recheck argument types and add casts if needed */ - recheck_cast_function_args(args, result_type, func_tuple); - - /* - * Lastly, we have to recursively simplify the defaults we just added (but - * don't recurse on the args passed in, as we already did those). This - * isn't merely an optimization, it's *necessary* since there could be - * functions with named or defaulted arguments down in there. - * - * Note that we do this last in hopes of simplifying any typecasts that - * were added by recheck_cast_function_args --- there shouldn't be any new - * casts added to the explicit arguments, but casts on the defaults are - * possible. - */ - foreach(lc, args) - { - if (nargsprovided-- > 0) - continue; /* skip original arg positions */ - lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc), - context); - } - return args; } --- 3812,3817 ----
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers