diff options
Diffstat (limited to 'src/backend/optimizer/util/clauses.c')
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 326 |
1 files changed, 255 insertions, 71 deletions
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index f2038c73af1..dcfc731a17a 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.278 2009/07/20 00:24:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.279 2009/10/08 02:39:21 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -95,11 +95,18 @@ static List *simplify_and_arguments(List *args, static Expr *simplify_boolean_equality(Oid opno, List *args); static Expr *simplify_function(Oid funcid, Oid result_type, int32 result_typmod, 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); static Expr *evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, List *args, HeapTuple func_tuple, @@ -2003,7 +2010,8 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum, * OR clauses into N-argument form. See comments in prepqual.c. * * NOTE: another critical effect is that any function calls that require - * default arguments will be expanded. + * default arguments will be expanded, and named-argument calls will be + * converted to positional notation. The executor won't handle either. *-------------------- */ Node * @@ -2113,17 +2121,26 @@ eval_const_expressions_mutator(Node *node, { FuncExpr *expr = (FuncExpr *) node; List *args; + bool has_named_args; Expr *simple; FuncExpr *newexpr; + ListCell *lc; /* - * Reduce constants in the FuncExpr'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. + * Reduce constants in the FuncExpr's arguments, and check to see + * if there are any named args. */ - args = (List *) expression_tree_mutator((Node *) expr->args, - eval_const_expressions_mutator, - (void *) context); + 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 @@ -2134,14 +2151,15 @@ eval_const_expressions_mutator(Node *node, simple = simplify_function(expr->funcid, expr->funcresulttype, exprTypmod(node), &args, - true, context); + has_named_args, true, context); if (simple) /* successfully simplified it */ return (Node *) simple; /* * The expression cannot be simplified any further, so build and * return a replacement FuncExpr node using the possibly-simplified - * arguments. + * arguments. Note that we have also converted the argument list + * to positional notation. */ newexpr = makeNode(FuncExpr); newexpr->funcid = expr->funcid; @@ -2181,7 +2199,7 @@ eval_const_expressions_mutator(Node *node, simple = simplify_function(expr->opfuncid, expr->opresulttype, -1, &args, - true, context); + false, true, context); if (simple) /* successfully simplified it */ return (Node *) simple; @@ -2274,7 +2292,7 @@ eval_const_expressions_mutator(Node *node, simple = simplify_function(expr->opfuncid, expr->opresulttype, -1, &args, - false, context); + false, false, context); if (simple) /* successfully simplified it */ { /* @@ -2466,7 +2484,7 @@ eval_const_expressions_mutator(Node *node, simple = simplify_function(outfunc, CSTRINGOID, -1, &args, - true, context); + false, true, context); if (simple) /* successfully simplified output fn */ { /* @@ -2484,7 +2502,7 @@ eval_const_expressions_mutator(Node *node, simple = simplify_function(infunc, expr->resulttype, -1, &args, - true, context); + false, true, context); if (simple) /* successfully simplified input fn */ return (Node *) simple; } @@ -3241,15 +3259,16 @@ simplify_boolean_equality(Oid opno, 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. + * This function is also responsible for converting named-notation argument + * lists into positional notation and/or adding any needed default argument + * expressions; 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, + bool has_named_args, bool allow_inline, eval_const_expressions_context *context) { @@ -3270,8 +3289,14 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod, if (!HeapTupleIsValid(func_tuple)) elog(ERROR, "cache lookup failed for function %u", funcid); - /* While we have the tuple, check if we need to add defaults */ - if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args)) + /* + * 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, *args, @@ -3287,12 +3312,112 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod, } /* + * 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; + + Assert(nargsprovided <= pronargs); + if (pronargs > FUNC_MAX_ARGS) + elog(ERROR, "too many function arguments"); + MemSet(argarray, 0, pronargs * sizeof(Node *)); + + /* Deconstruct the argument list into an array indexed by argnumber */ + i = 0; + foreach(lc, args) + { + Node *arg = (Node *) lfirst(lc); + + if (!IsA(arg, NamedArgExpr)) + { + /* positional argument, assumed to precede all named args */ + Assert(argarray[i] == NULL); + argarray[i++] = arg; + } + else + { + NamedArgExpr *na = (NamedArgExpr *) arg; + + Assert(argarray[na->argnumber] == NULL); + argarray[na->argnumber] = (Node *) na->arg; + } + } + + /* + * 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); + + i = pronargs - funcform->pronargdefaults; + foreach(lc, defaults) + { + if (argarray[i] == NULL) + { + argarray[i] = (Node *) lfirst(lc); + defargnumbers = bms_add_member(defargnumbers, i); + } + i++; + } + } + + /* Now reconstruct the args list in proper order */ + args = NIL; + for (i = 0; i < pronargs; i++) + { + Assert(argarray[i] != NULL); + 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; +} + +/* * 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. + * This is used only when the argument list was positional to begin with, + * 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, @@ -3300,41 +3425,96 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple, { Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); int nargsprovided = list_length(args); - 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); + /* 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) defaults = list_delete_first(defaults); + /* And form the combined argument list */ args = list_concat(args, defaults); - Assert(list_length(args) == funcform->pronargs); + + /* Recheck argument types and add casts if needed */ + recheck_cast_function_args(args, result_type, func_tuple); /* - * The next part should be a no-op if there are no polymorphic arguments, - * but we do it anyway to be sure. + * 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; +} + +/* + * fetch_function_defaults: get function's default arguments as expression list + */ +static List * +fetch_function_defaults(HeapTuple func_tuple) +{ + List *defaults; + Datum proargdefaults; + bool isnull; + char *str; + + /* 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); + return defaults; +} + +/* + * recheck_cast_function_args: recheck function args and typecast as needed + * after adding 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. + * + * This should be a no-op if there are no polymorphic arguments, + * but we do it anyway to be sure. + * + * Note: if any casts are needed, the args list is modified in-place. + */ +static void +recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple) +{ + Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); + int nargs; + Oid actual_arg_types[FUNC_MAX_ARGS]; + Oid declared_arg_types[FUNC_MAX_ARGS]; + Oid rettype; + ListCell *lc; + if (list_length(args) > FUNC_MAX_ARGS) elog(ERROR, "too many function arguments"); nargs = 0; @@ -3342,6 +3522,7 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple, { actual_arg_types[nargs++] = exprType((Node *) lfirst(lc)); } + Assert(nargs == funcform->pronargs); memcpy(declared_arg_types, funcform->proargtypes.values, funcform->pronargs * sizeof(Oid)); rettype = enforce_generic_type_consistency(actual_arg_types, @@ -3355,22 +3536,6 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple, /* perform any necessary typecasting of arguments */ make_fn_arguments(NULL, args, actual_arg_types, declared_arg_types); - - /* - * Lastly, we have to recursively simplify the arguments we just added - * (but don't recurse on the ones passed in, as we already did those). - * This isn't merely an optimization, it's *necessary* since there could - * be functions with defaulted arguments down in there. - */ - foreach(lc, args) - { - if (nargsprovided-- > 0) - continue; /* skip original arg positions */ - lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc), - context); - } - - return args; } /* @@ -3916,6 +4081,7 @@ Query * inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) { FuncExpr *fexpr; + Oid func_oid; HeapTuple func_tuple; Form_pg_proc funcform; Oid *argtypes; @@ -3944,6 +4110,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) fexpr = (FuncExpr *) rte->funcexpr; if (fexpr == NULL || !IsA(fexpr, FuncExpr)) return NULL; + func_oid = fexpr->funcid; /* * The function must be declared to return a set, else inlining would @@ -3967,17 +4134,17 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) return NULL; /* Check permission to call function (fail later, if not) */ - if (pg_proc_aclcheck(fexpr->funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) + if (pg_proc_aclcheck(func_oid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) return NULL; /* * OK, let's take a look at the function's pg_proc entry. */ func_tuple = SearchSysCache(PROCOID, - ObjectIdGetDatum(fexpr->funcid), + ObjectIdGetDatum(func_oid), 0, 0, 0); if (!HeapTupleIsValid(func_tuple)) - elog(ERROR, "cache lookup failed for function %u", fexpr->funcid); + elog(ERROR, "cache lookup failed for function %u", func_oid); funcform = (Form_pg_proc) GETSTRUCT(func_tuple); /* @@ -3985,16 +4152,15 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) * properties. In particular it mustn't be declared STRICT, since we * couldn't enforce that. It also mustn't be VOLATILE, because that is * supposed to cause it to be executed with its own snapshot, rather than - * sharing the snapshot of the calling query. (The nargs check is just - * paranoia, ditto rechecking proretset.) + * sharing the snapshot of the calling query. (Rechecking proretset is + * just paranoia.) */ if (funcform->prolang != SQLlanguageId || funcform->proisstrict || funcform->provolatile == PROVOLATILE_VOLATILE || funcform->prosecdef || !funcform->proretset || - !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || - funcform->pronargs != list_length(fexpr->args)) + !heap_attisnull(func_tuple, Anum_pg_proc_proconfig)) { ReleaseSysCache(func_tuple); return NULL; @@ -4020,6 +4186,24 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) ALLOCSET_DEFAULT_MAXSIZE); oldcxt = MemoryContextSwitchTo(mycxt); + /* + * Run eval_const_expressions on the function call. This is necessary + * to ensure that named-argument notation is converted to positional + * notation and any default arguments are inserted. It's a bit of + * overkill for the arguments, since they'll get processed again later, + * but no harm will be done. + */ + fexpr = (FuncExpr *) eval_const_expressions(root, (Node *) fexpr); + + /* It should still be a call of the same function, but let's check */ + if (!IsA(fexpr, FuncExpr) || + fexpr->funcid != func_oid) + goto fail; + + /* Arg list length should now match the function */ + if (list_length(fexpr->args) != funcform->pronargs) + goto fail; + /* Check for polymorphic arguments, and substitute actual arg types */ argtypes = (Oid *) palloc(funcform->pronargs * sizeof(Oid)); memcpy(argtypes, funcform->proargtypes.values, @@ -4038,7 +4222,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) Anum_pg_proc_prosrc, &isNull); if (isNull) - elog(ERROR, "null prosrc for function %u", fexpr->funcid); + elog(ERROR, "null prosrc for function %u", func_oid); src = TextDatumGetCString(tmp); /* @@ -4076,7 +4260,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) * shows it's returning a whole tuple result; otherwise what it's * returning is a single composite column which is not what we need. */ - if (!check_sql_fn_retval(fexpr->funcid, fexpr->funcresulttype, + if (!check_sql_fn_retval(func_oid, fexpr->funcresulttype, querytree_list, true, NULL) && (get_typtype(fexpr->funcresulttype) == TYPTYPE_COMPOSITE || @@ -4116,7 +4300,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) * Since there is now no trace of the function in the plan tree, we must * explicitly record the plan's dependency on the function. */ - record_plan_function_dependency(root->glob, fexpr->funcid); + record_plan_function_dependency(root->glob, func_oid); return querytree; |