aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util/clauses.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/util/clauses.c')
-rw-r--r--src/backend/optimizer/util/clauses.c326
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;